From 47399a6e2bcd5504d79c0fcf7ac9b89287bfac69 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Wed, 9 Jan 2019 12:15:58 +0100 Subject: [PATCH] Split Reference types doc into new file --- docs/conf.py | 2 +- docs/types.rst | 410 +-------------------------------- docs/types/reference-types.rst | 409 ++++++++++++++++++++++++++++++++ 3 files changed, 411 insertions(+), 410 deletions(-) create mode 100644 docs/types/reference-types.rst diff --git a/docs/conf.py b/docs/conf.py index 342aefa9b..d08a51918 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -81,7 +81,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts'] +exclude_patterns = ['_build', 'contracts', 'types'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/types.rst b/docs/types.rst index 4eb1f926e..cdb424d17 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -727,415 +727,7 @@ Another example that uses external function types:: .. note:: Lambda or inline functions are planned but not yet supported. -.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct - -.. _reference-types: - -Reference Types -=============== - -Values of reference type can be modified through multiple different names. -Contrast this with value types where you get an independent copy whenever -a variable of value type is used. Because of that, reference types have to be handled -more carefully than value types. Currently, reference types comprise structs, -arrays and mappings. If you use a reference type, you always have to explicitly -provide the data area where the type is stored: ``memory`` (whose lifetime is limited -to a function call), ``storage`` (the location where the state variables are stored) -or ``calldata`` (special data location that contains the function arguments, -only available for external function call parameters). - -An assignment or type conversion that changes the data location will always incur an automatic copy operation, -while assignments inside the same data location only copy in some cases for storage types. - -.. _data-location: - -Data location -------------- - -Every reference type, i.e. *arrays* and *structs*, has an additional -annotation, the "data location", about where it is stored. There are three data locations: -``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract -functions and is required for this type of parameter. Calldata is a non-modifiable, -non-persistent area where function arguments are stored, and behaves mostly like memory. - - -.. note:: - Prior to version 0.5.0 the data location could be omitted, and would default to different locations - depending on the kind of variable, function type, etc., but all complex types must now give an explicit - data location. - -.. _data-location-assignment: - -Data location and assignment behaviour -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 variable also only assign a reference. -* 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. - -:: - - pragma solidity >=0.4.0 <0.6.0; - - contract C { - uint[] x; // the data location of x is storage - - // the data location of memoryArray is memory - function f(uint[] memory memoryArray) public { - x = memoryArray; // works, copies the whole array to storage - uint[] storage y = x; // works, assigns a pointer, data location of y is storage - y[7]; // fine, returns the 8th element - y.length = 2; // fine, modifies x through y - delete x; // fine, clears the array, also modifies y - // The following does not work; it would need to create a new temporary / - // unnamed array in storage, but storage is "statically" allocated: - // y = memoryArray; - // This does not work either, since it would "reset" the pointer, but there - // is no sensible location it could point to. - // delete y; - g(x); // calls g, handing over a reference to x - h(x); // calls h and creates an independent, temporary copy in memory - } - - function g(uint[] storage) internal pure {} - function h(uint[] memory) public pure {} - } - -.. index:: ! array - -.. _arrays: - -Arrays ------- - -Arrays can have a compile-time fixed size, or they can have a dynamic size. - -The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, -and an array of dynamic size as ``T[]``. - -For example, an array of 5 dynamic arrays of ``uint`` is written as -``uint[][5]``. The notation is reversed compared to some other languages. In -Solidity, ``X[3]`` is always an array containing three elements of type ``X``, -even if ``X`` is itself an array. This is not the case in other languages such -as C. - -Indices are zero-based, and access is in the opposite direction of the -declaration. - -For example, if you have a variable ``uint[][5] x memory``, you access the -second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the -third dynamic array, use ``x[2]``. Again, -if you have an array ``T[5] a`` for a type ``T`` that can also be an array, -then ``a[2]`` always has type ``T``. - -Array elements can be of any type, including mapping or struct. The general -restrictions for types apply, in that mappings can only be stored in the -``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types `. - -Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member ` to change the size (see below for caveats). -method or increase the ``.length`` :ref:`member ` to add elements. - -Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, -but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow -length or index access. - -You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, -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 number of bytes, -always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. - -.. note:: - If you want to access the byte-representation of a string ``s``, use - ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind - that you are accessing the low-level bytes of the UTF-8 representation, - and not the individual characters. - -It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter `. -The numeric index becomes a required parameter for the getter. - -.. index:: ! array;allocating, new - -Allocating Memory Arrays -^^^^^^^^^^^^^^^^^^^^^^^^ - -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. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f(uint len) public pure { - uint[] memory a = new uint[](7); - bytes memory b = new bytes(len); - assert(a.length == 7); - assert(b.length == len); - a[6] = 8; - } - } - -.. index:: ! array;literals, ! inline;arrays - -Array Literals -^^^^^^^^^^^^^^ - -An array literal is a comma-separated list of one or more expressions, enclosed -in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a -common type all elements can be implicitly converted to. This is the elementary -type of the array. - -Array literals are always statically-sized memory arrays. - -In the example below, the type of ``[1, 2, 3]`` is -``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract C { - function f() public pure { - g([uint(1), 2, 3]); - } - function g(uint[3] memory) public pure { - // ... - } - } - -Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: - -:: - - pragma solidity >=0.4.0 <0.6.0; - - // This will not compile. - contract C { - function f() public { - // The next line creates a type error because uint[3] memory - // cannot be converted to uint[] memory. - uint[] memory x = [uint(1), 3, 4]; - } - } - -It is planned to remove this restriction in the future, but it creates some -complications because of how arrays are passed in the ABI. - -.. index:: ! array;length, length, push, pop, !array;push, !array;pop - -.. _array-members: - -Array Members -^^^^^^^^^^^^^ - -**length**: - 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. If you try to resize a non-dynamic array that isn't in - storage, you receive a ``Value must be an lvalue`` error. -**push**: - 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 you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. - -.. warning:: - 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 - dynamic arrays return from function calls. If you call functions - that return dynamic arrays, make sure to use an EVM that is set to - Byzantium mode. - -:: - - pragma solidity >=0.4.16 <0.6.0; - - contract ArrayContract { - uint[2**20] m_aLotOfIntegers; - // Note that the following is not a pair of dynamic arrays but a - // 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 contract function arguments - function setAllFlagPairs(bool[2][] memory newPairs) public { - // 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; - m_pairsOfFlags[index][1] = flagB; - } - - function changeFlagArraySize(uint newSize) public { - // if the new size is smaller, removed array elements will be cleared - m_pairsOfFlags.length = newSize; - } - - function clear() public { - // these clear the arrays completely - delete m_pairsOfFlags; - delete m_aLotOfIntegers; - // identical effect here - m_pairsOfFlags.length = 0; - } - - bytes m_byteData; - - function byteArrays(bytes memory data) public { - // byte arrays ("bytes") are different as they are stored without padding, - // but can be treated identical to "uint8[]" - m_byteData = data; - m_byteData.length += 7; - m_byteData[3] = 0x08; - delete m_byteData[2]; - } - - function addFlag(bool[2] memory flag) public returns (uint) { - return m_pairsOfFlags.push(flag); - } - - function createMemoryArray(uint size) public pure returns (bytes memory) { - // Dynamic memory arrays are created using `new`: - uint[2][] memory arrayOfPairs = new uint[2][](size); - - // Inline arrays are always statically-sized and if you only - // use literals, you have to provide at least one type. - arrayOfPairs[0] = [uint(1), 2]; - - // Create a dynamic byte array: - bytes memory b = new bytes(200); - for (uint i = 0; i < b.length; i++) - b[i] = byte(uint8(i)); - return b; - } - } - - -.. index:: ! struct, ! type;struct - -.. _structs: - -Structs -------- - -Solidity provides a way to define new types in the form of structs, which is -shown in the following example: - -:: - - pragma solidity >=0.4.11 <0.6.0; - - contract CrowdFunding { - // Defines a new type with two fields. - struct Funder { - address addr; - uint amount; - } - - struct Campaign { - address payable beneficiary; - uint fundingGoal; - uint numFunders; - uint amount; - mapping (uint => Funder) funders; - } - - uint numCampaigns; - mapping (uint => Campaign) campaigns; - - function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { - campaignID = numCampaigns++; // campaignID is return variable - // 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); - } - - function contribute(uint campaignID) public payable { - Campaign storage c = campaigns[campaignID]; - // Creates a new temporary memory struct, initialised with the given values - // and copies it over to storage. - // Note that you can also use Funder(msg.sender, msg.value) to initialise. - c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); - c.amount += msg.value; - } - - function checkGoalReached(uint campaignID) public returns (bool reached) { - Campaign storage c = campaigns[campaignID]; - if (c.amount < c.fundingGoal) - return false; - uint amount = c.amount; - c.amount = 0; - c.beneficiary.transfer(amount); - return true; - } - } - -The contract does not provide the full functionality of a crowdfunding -contract, but it contains the basic concepts necessary to understand structs. -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 -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 -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. - -Of course, you can also directly access the members of the struct without -assigning it to a local variable, as in -``campaigns[campaignID].amount = 0``. +.. include:: types/reference-types.rst .. index:: !mapping .. _mapping-types: diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst new file mode 100644 index 000000000..b133bdf12 --- /dev/null +++ b/docs/types/reference-types.rst @@ -0,0 +1,409 @@ +.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct + +.. _reference-types: + +Reference Types +=============== + +Values of reference type can be modified through multiple different names. +Contrast this with value types where you get an independent copy whenever +a variable of value type is used. Because of that, reference types have to be handled +more carefully than value types. Currently, reference types comprise structs, +arrays and mappings. If you use a reference type, you always have to explicitly +provide the data area where the type is stored: ``memory`` (whose lifetime is limited +to a function call), ``storage`` (the location where the state variables are stored) +or ``calldata`` (special data location that contains the function arguments, +only available for external function call parameters). + +An assignment or type conversion that changes the data location will always incur an automatic copy operation, +while assignments inside the same data location only copy in some cases for storage types. + +.. _data-location: + +Data location +------------- + +Every reference type, i.e. *arrays* and *structs*, has an additional +annotation, the "data location", about where it is stored. There are three data locations: +``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract +functions and is required for this type of parameter. Calldata is a non-modifiable, +non-persistent area where function arguments are stored, and behaves mostly like memory. + + +.. note:: + Prior to version 0.5.0 the data location could be omitted, and would default to different locations + depending on the kind of variable, function type, etc., but all complex types must now give an explicit + data location. + +.. _data-location-assignment: + +Data location and assignment behaviour +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +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 variable also only assign a reference. +* 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. + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract C { + uint[] x; // the data location of x is storage + + // the data location of memoryArray is memory + function f(uint[] memory memoryArray) public { + x = memoryArray; // works, copies the whole array to storage + uint[] storage y = x; // works, assigns a pointer, data location of y is storage + y[7]; // fine, returns the 8th element + y.length = 2; // fine, modifies x through y + delete x; // fine, clears the array, also modifies y + // The following does not work; it would need to create a new temporary / + // unnamed array in storage, but storage is "statically" allocated: + // y = memoryArray; + // This does not work either, since it would "reset" the pointer, but there + // is no sensible location it could point to. + // delete y; + g(x); // calls g, handing over a reference to x + h(x); // calls h and creates an independent, temporary copy in memory + } + + function g(uint[] storage) internal pure {} + function h(uint[] memory) public pure {} + } + +.. index:: ! array + +.. _arrays: + +Arrays +------ + +Arrays can have a compile-time fixed size, or they can have a dynamic size. + +The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, +and an array of dynamic size as ``T[]``. + +For example, an array of 5 dynamic arrays of ``uint`` is written as +``uint[][5]``. The notation is reversed compared to some other languages. In +Solidity, ``X[3]`` is always an array containing three elements of type ``X``, +even if ``X`` is itself an array. This is not the case in other languages such +as C. + +Indices are zero-based, and access is in the opposite direction of the +declaration. + +For example, if you have a variable ``uint[][5] x memory``, you access the +second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the +third dynamic array, use ``x[2]``. Again, +if you have an array ``T[5] a`` for a type ``T`` that can also be an array, +then ``a[2]`` always has type ``T``. + +Array elements can be of any type, including mapping or struct. The general +restrictions for types apply, in that mappings can only be stored in the +``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types `. + +Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member ` to change the size (see below for caveats). +method or increase the ``.length`` :ref:`member ` to add elements. + +Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow +length or index access. + +You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule, +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 number of bytes, +always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper. + +.. note:: + If you want to access the byte-representation of a string ``s``, use + ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind + that you are accessing the low-level bytes of the UTF-8 representation, + and not the individual characters. + +It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter `. +The numeric index becomes a required parameter for the getter. + +.. index:: ! array;allocating, new + +Allocating Memory Arrays +^^^^^^^^^^^^^^^^^^^^^^^^ + +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. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + function f(uint len) public pure { + uint[] memory a = new uint[](7); + bytes memory b = new bytes(len); + assert(a.length == 7); + assert(b.length == len); + a[6] = 8; + } + } + +.. index:: ! array;literals, ! inline;arrays + +Array Literals +^^^^^^^^^^^^^^ + +An array literal is a comma-separated list of one or more expressions, enclosed +in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a +common type all elements can be implicitly converted to. This is the elementary +type of the array. + +Array literals are always statically-sized memory arrays. + +In the example below, the type of ``[1, 2, 3]`` is +``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract C { + function f() public pure { + g([uint(1), 2, 3]); + } + function g(uint[3] memory) public pure { + // ... + } + } + +Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + // This will not compile. + contract C { + function f() public { + // The next line creates a type error because uint[3] memory + // cannot be converted to uint[] memory. + uint[] memory x = [uint(1), 3, 4]; + } + } + +It is planned to remove this restriction in the future, but it creates some +complications because of how arrays are passed in the ABI. + +.. index:: ! array;length, length, push, pop, !array;push, !array;pop + +.. _array-members: + +Array Members +^^^^^^^^^^^^^ + +**length**: + 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. If you try to resize a non-dynamic array that isn't in + storage, you receive a ``Value must be an lvalue`` error. +**push**: + 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 you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. + +.. warning:: + 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 + dynamic arrays return from function calls. If you call functions + that return dynamic arrays, make sure to use an EVM that is set to + Byzantium mode. + +:: + + pragma solidity >=0.4.16 <0.6.0; + + contract ArrayContract { + uint[2**20] m_aLotOfIntegers; + // Note that the following is not a pair of dynamic arrays but a + // 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 contract function arguments + function setAllFlagPairs(bool[2][] memory newPairs) public { + // 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; + m_pairsOfFlags[index][1] = flagB; + } + + function changeFlagArraySize(uint newSize) public { + // if the new size is smaller, removed array elements will be cleared + m_pairsOfFlags.length = newSize; + } + + function clear() public { + // these clear the arrays completely + delete m_pairsOfFlags; + delete m_aLotOfIntegers; + // identical effect here + m_pairsOfFlags.length = 0; + } + + bytes m_byteData; + + function byteArrays(bytes memory data) public { + // byte arrays ("bytes") are different as they are stored without padding, + // but can be treated identical to "uint8[]" + m_byteData = data; + m_byteData.length += 7; + m_byteData[3] = 0x08; + delete m_byteData[2]; + } + + function addFlag(bool[2] memory flag) public returns (uint) { + return m_pairsOfFlags.push(flag); + } + + function createMemoryArray(uint size) public pure returns (bytes memory) { + // Dynamic memory arrays are created using `new`: + uint[2][] memory arrayOfPairs = new uint[2][](size); + + // Inline arrays are always statically-sized and if you only + // use literals, you have to provide at least one type. + arrayOfPairs[0] = [uint(1), 2]; + + // Create a dynamic byte array: + bytes memory b = new bytes(200); + for (uint i = 0; i < b.length; i++) + b[i] = byte(uint8(i)); + return b; + } + } + + +.. index:: ! struct, ! type;struct + +.. _structs: + +Structs +------- + +Solidity provides a way to define new types in the form of structs, which is +shown in the following example: + +:: + + pragma solidity >=0.4.11 <0.6.0; + + contract CrowdFunding { + // Defines a new type with two fields. + struct Funder { + address addr; + uint amount; + } + + struct Campaign { + address payable beneficiary; + uint fundingGoal; + uint numFunders; + uint amount; + mapping (uint => Funder) funders; + } + + uint numCampaigns; + mapping (uint => Campaign) campaigns; + + function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { + campaignID = numCampaigns++; // campaignID is return variable + // 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); + } + + function contribute(uint campaignID) public payable { + Campaign storage c = campaigns[campaignID]; + // Creates a new temporary memory struct, initialised with the given values + // and copies it over to storage. + // Note that you can also use Funder(msg.sender, msg.value) to initialise. + c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); + c.amount += msg.value; + } + + function checkGoalReached(uint campaignID) public returns (bool reached) { + Campaign storage c = campaigns[campaignID]; + if (c.amount < c.fundingGoal) + return false; + uint amount = c.amount; + c.amount = 0; + c.beneficiary.transfer(amount); + return true; + } + } + +The contract does not provide the full functionality of a crowdfunding +contract, but it contains the basic concepts necessary to understand structs. +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 +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 +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. + +Of course, you can also directly access the members of the struct without +assigning it to a local variable, as in +``campaigns[campaignID].amount = 0``.