mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5032 from ethereum/moreRef
[DOCS] More on reference types.
This commit is contained in:
commit
391cb0bb53
116
docs/types.rst
116
docs/types.rst
@ -596,8 +596,8 @@ also accepts a payment of zero Ether, so it also is ``non-payable``.
|
|||||||
On the other hand, a ``non-payable`` function will reject Ether sent to it,
|
On the other hand, a ``non-payable`` function will reject Ether sent to it,
|
||||||
so ``non-payable`` functions cannot be converted to ``payable`` functions.
|
so ``non-payable`` functions cannot be converted to ``payable`` functions.
|
||||||
|
|
||||||
If a function type variable is not initialized, calling it will result
|
If a function type variable is not initialised, calling it results
|
||||||
in an exception. The same happens if you call a function after using ``delete``
|
in a failed assertion. The same happens if you call a function after using ``delete``
|
||||||
on it.
|
on it.
|
||||||
|
|
||||||
If external function types are used outside of the context of Solidity,
|
If external function types are used outside of the context of Solidity,
|
||||||
@ -746,14 +746,14 @@ non-persistent area where function arguments are stored, and behaves mostly like
|
|||||||
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
||||||
data location.
|
data location.
|
||||||
|
|
||||||
Data locations are important because they change how assignments behave:
|
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
|
||||||
assignments between storage and memory and also to a state variable (even from other state variables)
|
assignments between storage and memory (or from calldata) always create an independent copy.
|
||||||
always create an independent copy.
|
Assignments from memory to memory only create references. This means that changes to one memory variable
|
||||||
Assignments to local storage variables only assign a reference though, and
|
are also visible in all other memory variables that refer to the same data.
|
||||||
this reference always points to the state variable even if the latter is changed
|
Assignments from storage to a local storage variables also only assign a reference.
|
||||||
in the meantime.
|
In contrast, all other assignments to storage always copy. Examples for this case
|
||||||
On the other hand, assignments from a memory stored reference type to another
|
are assignments to state variables or to members of local variables of storage struct type, even
|
||||||
memory-stored reference type do not create a copy.
|
if the local variable itself is just a reference.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -783,13 +783,6 @@ memory-stored reference type do not create a copy.
|
|||||||
function h(uint[] memory) public pure {}
|
function h(uint[] memory) public pure {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Summary
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
Forced data location:
|
|
||||||
- parameters (not return) of external functions: calldata
|
|
||||||
- state variables: storage
|
|
||||||
|
|
||||||
.. index:: ! array
|
.. index:: ! array
|
||||||
|
|
||||||
.. _arrays:
|
.. _arrays:
|
||||||
@ -798,9 +791,10 @@ Arrays
|
|||||||
------
|
------
|
||||||
|
|
||||||
Arrays can have a compile-time fixed size or they can be dynamic.
|
Arrays can have a compile-time fixed size or they can be dynamic.
|
||||||
For storage arrays, the element type can be arbitrary (i.e. also other
|
The are few restrictions for the element, it can also be
|
||||||
arrays, mappings or structs). For memory arrays, it cannot be a mapping and
|
another array, a mapping or a struct. The general restrictions for
|
||||||
has to be an ABI type if it is an argument of a publicly-visible function.
|
types apply, though, in that mappings can only be used in storage
|
||||||
|
and publicly-visible functions need parameters that are ABI types.
|
||||||
|
|
||||||
An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
|
An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
|
||||||
an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic
|
an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic
|
||||||
@ -810,9 +804,13 @@ third dynamic array, you use ``x[2][1]`` (indices are zero-based and
|
|||||||
access works in the opposite way of the declaration, i.e. ``x[2]``
|
access works in the opposite way of the declaration, i.e. ``x[2]``
|
||||||
shaves off one level in the type from the right).
|
shaves off one level in the type from the right).
|
||||||
|
|
||||||
|
Accessing an array past its end causes a revert. If you want to add
|
||||||
|
new elements, you have to use ``.push()`` or increase the ``.length``
|
||||||
|
member (see below).
|
||||||
|
|
||||||
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
|
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
|
||||||
but it is packed tightly in calldata. ``string`` is equal to ``bytes`` but does not allow
|
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
|
||||||
length or index access (for now).
|
length or index access.
|
||||||
So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper.
|
So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper.
|
||||||
As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string``
|
As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string``
|
||||||
for arbitrary-length string (UTF-8) data. If you can limit the length to a certain
|
for arbitrary-length string (UTF-8) data. If you can limit the length to a certain
|
||||||
@ -832,7 +830,7 @@ The numeric index will become a required parameter for the getter.
|
|||||||
Allocating Memory Arrays
|
Allocating Memory Arrays
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Creating arrays with variable length in memory can be done using the ``new`` keyword.
|
You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory.
|
||||||
As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to
|
As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to
|
||||||
the ``.length`` member). You either have to calculate the required size in advance
|
the ``.length`` member). You either have to calculate the required size in advance
|
||||||
or create a new memory array and copy every element.
|
or create a new memory array and copy every element.
|
||||||
@ -875,7 +873,7 @@ assigned to a variable right away.
|
|||||||
The type of an array literal is a memory array of fixed size whose base
|
The type of an array literal is a memory array of fixed size whose base
|
||||||
type is the common type of the given elements. The type of ``[1, 2, 3]`` is
|
type is the common type of the given elements. The type of ``[1, 2, 3]`` is
|
||||||
``uint8[3] memory``, because the type of each of these constants is ``uint8``.
|
``uint8[3] memory``, because the type of each of these constants is ``uint8``.
|
||||||
Because of that, it was necessary to convert the first element in the example
|
Because of that, it is necessary to convert the first element in the example
|
||||||
above to ``uint``. Note that currently, fixed size memory arrays cannot
|
above to ``uint``. Note that currently, fixed size memory arrays cannot
|
||||||
be assigned to dynamically-sized memory arrays, i.e. the following is not
|
be assigned to dynamically-sized memory arrays, i.e. the following is not
|
||||||
possible:
|
possible:
|
||||||
@ -902,15 +900,31 @@ Members
|
|||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
**length**:
|
**length**:
|
||||||
Arrays have a ``length`` member to read their number of elements.
|
Arrays have a ``length`` member that contains their number of elements.
|
||||||
Dynamically-sized arrays (only available for storage) have a read-write ``length`` member to resize the array. Increasing the length adds uninitialized elements to the array, this has *O(1)* complexity. Reducing the length performs :ref:``delete`` on each removed element and has *O(n)* complexity where *n* is the number of elements being deleted. Please note that calling ``length--`` on an empty array will set the length of the array to 2^256-1 due to ``uint256`` underflow wrapping.
|
The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created.
|
||||||
|
For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array.
|
||||||
|
Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion.
|
||||||
|
Increasing the length adds new zero-initialised elements to the array.
|
||||||
|
Reducing the length performs an implicit :ref:``delete`` on each of the removed elements.
|
||||||
**push**:
|
**push**:
|
||||||
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that can be used to append an element at the end of the array. The function returns the new length.
|
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length.
|
||||||
**pop**:
|
**pop**:
|
||||||
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that can be used to remove an element from the end of the array. This will also implicitly call :ref:``delete`` on the removed element.
|
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
It is not yet possible to use arrays of arrays in external functions.
|
If you use ``.length--`` on an empty array, it causes an underflow and
|
||||||
|
thus sets the length to ``2**256-1``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Increasing the length of a storage array has constant gas costs because
|
||||||
|
storage is assumed to be zero-initialised, while decreasing
|
||||||
|
the length has at least linear cost (but in most cases worse than linear),
|
||||||
|
because it includes explicitly clearing the removed
|
||||||
|
elements similar to calling :ref:``delete`` on them.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
It is not yet possible to use arrays of arrays in external functions
|
||||||
|
(but they are supported in public functions).
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
In EVM versions before Byzantium, it was not possible to access
|
In EVM versions before Byzantium, it was not possible to access
|
||||||
@ -928,15 +942,34 @@ Members
|
|||||||
// dynamic array of pairs (i.e. of fixed size arrays of length two).
|
// dynamic array of pairs (i.e. of fixed size arrays of length two).
|
||||||
// Because of that, T[] is always a dynamic array of T, even if T
|
// Because of that, T[] is always a dynamic array of T, even if T
|
||||||
// itself is an array.
|
// itself is an array.
|
||||||
|
// Data location for all state variables is storage.
|
||||||
bool[2][] m_pairsOfFlags;
|
bool[2][] m_pairsOfFlags;
|
||||||
|
|
||||||
// newPairs is stored in memory - the only possibility
|
// newPairs is stored in memory - the only possibility
|
||||||
// for public function arguments
|
// for public contract function arguments
|
||||||
function setAllFlagPairs(bool[2][] memory newPairs) public {
|
function setAllFlagPairs(bool[2][] memory newPairs) public {
|
||||||
// assignment to a storage array replaces the complete array
|
// assignment to a storage array performs a copy of ``newPairs`` and
|
||||||
|
// replaces the complete array ``m_pairsOfFlags``.
|
||||||
m_pairsOfFlags = newPairs;
|
m_pairsOfFlags = newPairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StructType {
|
||||||
|
uint[] contents;
|
||||||
|
uint moreInfo;
|
||||||
|
}
|
||||||
|
StructType s;
|
||||||
|
|
||||||
|
function f(uint[] memory c) public {
|
||||||
|
// stores a reference to ``s`` in ``g``
|
||||||
|
StructType storage g = s;
|
||||||
|
// also changes ``s.moreInfo``.
|
||||||
|
g.moreInfo = 2;
|
||||||
|
// assigns a copy because ``g.contents``
|
||||||
|
// is not a local variable, but a member of
|
||||||
|
// a local variable.
|
||||||
|
g.contents = c;
|
||||||
|
}
|
||||||
|
|
||||||
function setFlagPair(uint index, bool flagA, bool flagB) public {
|
function setFlagPair(uint index, bool flagA, bool flagB) public {
|
||||||
// access to a non-existing index will throw an exception
|
// access to a non-existing index will throw an exception
|
||||||
m_pairsOfFlags[index][0] = flagA;
|
m_pairsOfFlags[index][0] = flagA;
|
||||||
@ -1022,7 +1055,10 @@ shown in the following example:
|
|||||||
|
|
||||||
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
|
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
|
||||||
campaignID = numCampaigns++; // campaignID is return variable
|
campaignID = numCampaigns++; // campaignID is return variable
|
||||||
// Creates new struct and saves in storage. We leave out the mapping type.
|
// Creates new struct in memory and copies it to storage.
|
||||||
|
// We leave out the mapping type, because it is not valid in memory.
|
||||||
|
// If structs are copied (even from storage to storage), mapping types
|
||||||
|
// are always omitted, because they cannot be enumerated.
|
||||||
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
|
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1052,11 +1088,12 @@ Struct types can be used inside mappings and arrays and they can itself
|
|||||||
contain mappings and arrays.
|
contain mappings and arrays.
|
||||||
|
|
||||||
It is not possible for a struct to contain a member of its own type,
|
It is not possible for a struct to contain a member of its own type,
|
||||||
although the struct itself can be the value type of a mapping member.
|
although the struct itself can be the value type of a mapping member
|
||||||
|
or it can contain a dynamically-sized array of its type.
|
||||||
This restriction is necessary, as the size of the struct has to be finite.
|
This restriction is necessary, as the size of the struct has to be finite.
|
||||||
|
|
||||||
Note how in all the functions, a struct type is assigned to a local variable
|
Note how in all the functions, a struct type is assigned to a local variable
|
||||||
(of the default storage data location).
|
with data location ``storage``.
|
||||||
This does not copy the struct but only stores a reference so that assignments to
|
This does not copy the struct but only stores a reference so that assignments to
|
||||||
members of the local variable actually write to the state.
|
members of the local variable actually write to the state.
|
||||||
|
|
||||||
@ -1067,7 +1104,7 @@ assigning it to a local variable, as in
|
|||||||
.. index:: !mapping
|
.. index:: !mapping
|
||||||
|
|
||||||
Mappings
|
Mappings
|
||||||
========
|
--------
|
||||||
|
|
||||||
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
|
||||||
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
|
||||||
@ -1076,7 +1113,7 @@ or complex types like contract types, enums, mappings, structs and any array typ
|
|||||||
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.
|
||||||
|
|
||||||
You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialized
|
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.
|
||||||
@ -1084,8 +1121,11 @@ 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
|
||||||
value being set.
|
value being set.
|
||||||
|
|
||||||
Mappings are **only** allowed for state variables (or as storage reference types
|
Mappings can only have a data location of ``storage`` and thus
|
||||||
in internal functions).
|
are allowed for state variables, as storage reference types
|
||||||
|
in functions, or as parameters for library functions.
|
||||||
|
They cannot be used as parameters or return parameters
|
||||||
|
of contract functions that are publicly visible.
|
||||||
|
|
||||||
You can mark variables of mapping type as ``public`` and Solidity creates a
|
You can mark 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
|
||||||
|
Loading…
Reference in New Issue
Block a user