Explain mappings example, and add link to further details

Updates from review

Changes from review

Nested example

Update docs/types/mapping-types.rst

Co-Authored-By: chriseth <chris@ethereum.org>
Changes from review

Bring example inline with ERC20

Clarify what maps where

Use OZ contract example

Update docs/types/mapping-types.rst

Co-Authored-By: chriseth <chris@ethereum.org>

update code example
This commit is contained in:
Chris Chinchilla 2019-10-18 10:16:03 +02:00 committed by chriseth
parent aa8de4404e
commit 0c51dcc967
2 changed files with 58 additions and 6 deletions

View File

@ -4,7 +4,8 @@
Mapping Types Mapping Types
============= =============
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``.
The ``_KeyType`` can be any elementary type. This means it can be any of The ``_KeyType`` can be any elementary type. This means it can be any of
the built-in value types plus ``bytes`` and ``string``. User-defined the built-in value types plus ``bytes`` and ``string``. User-defined
or complex types like contract types, enums, mappings, structs and any array type or complex types like contract types, enums, mappings, structs and any array type
@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters
of contract functions that are publicly visible. of contract functions that are publicly visible.
You can mark state variables of mapping type as ``public`` and Solidity creates a You can mark state variables of mapping type as ``public`` and Solidity creates a
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a :ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.
parameter for the getter. If ``_ValueType`` is a value type or a struct, If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``.
the getter returns ``_ValueType``.
If ``_ValueType`` is an array or a mapping, the getter has one parameter for If ``_ValueType`` is an array or a mapping, the getter has one parameter for
each ``_KeyType``, recursively. For example with a mapping: each ``_KeyType``, recursively.
In the example below, the ``MappingExample`` contract defines a public ``balances``
mapping, with the key type an ``address``, and a value type a ``uint``, mapping
an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter
returns a value that matches the type, which you can see in the ``MappingUser``
contract that returns the value at the specified address.
:: ::
@ -53,6 +59,52 @@ each ``_KeyType``, recursively. For example with a mapping:
} }
} }
The example below is a simplified version of an `ERC20 token <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>`_.
``_allowances`` is an example of a mapping type inside another mapping type.
The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
::
pragma solidity >=0.4.0 <0.7.0;
contract MappingExample {
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
approve(sender, msg.sender, amount);
return true;
}
function approve(address owner, address spender, uint256 amount) public returns (bool) {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] -= amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
}
.. index:: !iterable mappings .. index:: !iterable mappings
.. _iterable-mappings: .. _iterable-mappings:

View File

@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d
array of length zero or a static array of the same length with all elements set to their array of length zero or a static array of the same length with all elements set to their
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
all other elements and the length of the array untouched. This especially means that it leaves all other elements and the length of the array untouched. This especially means that it leaves
a gap in the array. If you plan to remove items, a mapping is probably a better choice. a gap in the array. If you plan to remove items, a :ref:`mapping <mapping-types>` is probably a better choice.
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: