Merge pull request #5032 from ethereum/moreRef

[DOCS] More on reference types.
This commit is contained in:
chriseth 2018-09-27 11:17:44 +02:00 committed by GitHub
commit 391cb0bb53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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,
so ``non-payable`` functions cannot be converted to ``payable`` functions.
If a function type variable is not initialized, calling it will result
in an exception. The same happens if you call a function after using ``delete``
If a function type variable is not initialised, calling it results
in a failed assertion. The same happens if you call a function after using ``delete``
on it.
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
data location.
Data locations are important because they change how assignments behave:
assignments between storage and memory and also to a state variable (even from other state variables)
always create an independent copy.
Assignments to local storage variables only assign a reference though, and
this reference always points to the state variable even if the latter is changed
in the meantime.
On the other hand, assignments from a memory stored reference type to another
memory-stored reference type do not create a copy.
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
assignments between storage and memory (or from calldata) always create an independent copy.
Assignments from memory to memory only create references. This means that changes to one memory variable
are also visible in all other memory variables that refer to the same data.
Assignments from storage to a local storage variables also only assign a reference.
In contrast, all other assignments to storage always copy. Examples for this case
are assignments to state variables or to members of local variables of storage struct type, even
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 {}
}
Summary
^^^^^^^
Forced data location:
- parameters (not return) of external functions: calldata
- state variables: storage
.. index:: ! array
.. _arrays:
@ -798,9 +791,10 @@ Arrays
------
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
arrays, mappings or structs). For memory arrays, it cannot be a mapping and
has to be an ABI type if it is an argument of a publicly-visible function.
The are few restrictions for the element, it can also be
another array, a mapping or a struct. The general restrictions for
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 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]``
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[]``,
but it is packed tightly in calldata. ``string`` is equal to ``bytes`` but does not allow
length or index access (for now).
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
length or index access.
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``
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
^^^^^^^^^^^^^^^^^^^^^^^^
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
the ``.length`` member). You either have to calculate the required size in advance
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
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``.
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
be assigned to dynamically-sized memory arrays, i.e. the following is not
possible:
@ -902,15 +900,31 @@ Members
^^^^^^^
**length**:
Arrays have a ``length`` member to read 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.
Arrays have a ``length`` member that contains their number of elements.
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**:
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**:
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::
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::
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).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
// Data location for all state variables is storage.
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the only possibility
// for public function arguments
// for public contract function arguments
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;
}
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 {
// access to a non-existing index will throw an exception
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) {
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);
}
@ -1052,11 +1088,12 @@ Struct types can be used inside mappings and arrays and they can itself
contain mappings and arrays.
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.
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
members of the local variable actually write to the state.
@ -1067,7 +1104,7 @@ assigning it to a local variable, as in
.. index:: !mapping
Mappings
========
--------
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
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.
``_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
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.
@ -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
value being set.
Mappings are **only** allowed for state variables (or as storage reference types
in internal functions).
Mappings can only have a data location of ``storage`` and thus
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
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a