Merge pull request #8024 from ethereum/updateMapping06

Update mapping types.
This commit is contained in:
chriseth 2019-12-16 18:23:06 +01:00 committed by GitHub
commit 4d73864584
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,16 +5,17 @@ Mapping Types
============= =============
Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``. of mapping type are declared using the syntax ``mapping(_KeyType => _ValueType) _VariableName``.
The ``_KeyType`` can be any elementary type. This means it can be any of The ``_KeyType`` can be any
the built-in value types plus ``bytes`` and ``string``. User-defined built-in value type plus ``bytes`` and ``string``. User-defined
or complex types like contract types, enums, mappings, structs and any array type or complex types such as contract types, enums, mappings, structs or array types
apart from ``bytes`` and ``string`` are not allowed. apart from ``bytes`` and ``string`` are not allowed.
``_ValueType`` can be any type, including mappings. ``_ValueType`` can be any type, including mappings, arrays and structs.
You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised
such that every possible key exists and is mapped to a value whose such that every possible key exists and is mapped to a value whose
byte-representation is all zeros, a type's :ref:`default value <default-value>`. The similarity ends there, the key data is not stored in a byte-representation is all zeros, a type's :ref:`default value <default-value>`.
The similarity ends there, the key data is not stored in a
mapping, only its ``keccak256`` hash is used to look up the value. mapping, only its ``keccak256`` hash is used to look up the value.
Because of this, mappings do not have a length or a concept of a key or Because of this, mappings do not have a length or a concept of a key or
@ -59,7 +60,8 @@ contract that returns the value at the specified address.
} }
} }
The example below is a simplified version of an `ERC20 token <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>`_. 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. ``_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. The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
@ -111,26 +113,26 @@ The example below uses ``_allowances`` to record the amount someone else is allo
Iterable Mappings Iterable Mappings
----------------- -----------------
Mappings are not iterable, but it is possible to implement a data structure on You cannot iterate over mappings, i.e. you cannot enumerate their keys.
It is possible, though, to implement a data structure on
top of them and iterate over that. For example, the code below implements an top of them and iterate over that. For example, the code below implements an
``IterableMapping`` library that the ``User`` contract then adds data too, and ``IterableMapping`` library that the ``User`` contract then adds data too, and
the ``sum`` function iterates over to sum all the values. the ``sum`` function iterates over to sum all the values.
:: ::
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.5.99 <0.7.0;
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }
struct itmap {
mapping(uint => IndexValue) data;
KeyFlag[] keys;
uint size;
}
library IterableMapping { library IterableMapping {
struct itmap {
mapping(uint => IndexValue) data;
KeyFlag[] keys;
uint size;
}
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }
function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) { function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
uint keyIndex = self.data[key].keyIndex; uint keyIndex = self.data[key].keyIndex;
self.data[key].value = value; self.data[key].value = value;
@ -183,23 +185,28 @@ the ``sum`` function iterates over to sum all the values.
// How to use it // How to use it
contract User { contract User {
// Just a struct holding our data. // Just a struct holding our data.
IterableMapping.itmap data; itmap data;
// Apply library functions to the data type.
using IterableMapping for itmap;
// Insert something // Insert something
function insert(uint k, uint v) public returns (uint size) { function insert(uint k, uint v) public returns (uint size) {
// Actually calls itmap_impl.insert, auto-supplying the first parameter for us. // This calls IterableMapping.insert(data, k, v)
IterableMapping.insert(data, k, v); data.insert(k, v);
// We can still access members of the struct - but we should take care not to mess with them. // We can still access members of the struct,
// but we should take care not to mess with them.
return data.size; return data.size;
} }
// Computes the sum of all stored data. // Computes the sum of all stored data.
function sum() public view returns (uint s) { function sum() public view returns (uint s) {
for (uint i = IterableMapping.iterate_start(data); for (
IterableMapping.iterate_valid(data, i); uint i = data.iterate_start();
i = IterableMapping.iterate_next(data, i)) { data.iterate_valid(i);
(, uint value) = IterableMapping.iterate_get(data, i); i = data.iterate_next(i)
) {
(, uint value) = data.iterate_get(i);
s += value; s += value;
} }
} }
} }