Skip to content

Move type related FAQ points #4499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 0 additions & 154 deletions docs/frequently-asked-questions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,6 @@ This is a limitation of the EVM and will be solved with the next protocol update

Returning variably-sized data as part of an external transaction or call is fine.

Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];``
=========================================================================================

Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory
array in the return statement. Pretty cool, huh?

Example::

pragma solidity ^0.4.16;

contract C {
function f() public pure returns (uint8[5]) {
string[4] memory adaArr = ["This", "is", "an", "array"];
return ([1, 2, 3, 4, 5]);
}
}

Can a contract function return a ``struct``?
============================================

Expand Down Expand Up @@ -187,43 +170,6 @@ Is unused gas automatically refunded?

Yes and it is immediate, i.e. done as part of the transaction.

When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value?
============================================================================================================

This is not possible, because all types use up the full value range.

You have the option to ``throw`` on error, which will also revert the whole
transaction, which might be a good idea if you ran into an unexpected
situation.

If you do not want to throw, you can return a pair::

pragma solidity >0.4.23 <0.5.0;

contract C {
uint[] counters;

function getCounter(uint index)
public
view
returns (uint counter, bool error) {
if (index >= counters.length)
return (0, true);
else
return (counters[index], false);
}

function checkCounter(uint index) public view {
(uint counter, bool error) = getCounter(index);
if (error) {
// ...
} else {
// ...
}
}
}


Are comments included with deployed contracts and do they increase deployment gas?
==================================================================================

Expand Down Expand Up @@ -357,24 +303,6 @@ Note2: Optimizing storage access can pull the gas costs down considerably, becau
currently do not work across loops and also have a problem with bounds checking.
You might get much better results in the future, though.

What happens to a ``struct``'s mapping when copying over a ``struct``?
======================================================================

This is a very interesting question. Suppose that we have a contract field set up like such::

struct User {
mapping(string => string) comments;
}

function somefunction public {
User user1;
user1.comments["Hello"] = "World";
User user2 = user1;
}

In this case, the mapping of the struct being copied over into the userList is ignored as there is no "list of mapped keys".
Therefore it is not possible to find out which values should be copied over.

How do I initialize a contract with only a specific amount of wei?
==================================================================

Expand Down Expand Up @@ -404,28 +332,6 @@ Can a contract function accept a two-dimensional array?
This is not yet implemented for external calls and dynamic arrays -
you can only use one level of dynamic arrays.

What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean?
========================================================================================================================================================================

The type ``bytes32`` can hold 32 (raw) bytes. In the assignment ``bytes32 samevar = "stringliteral";``,
the string literal is interpreted in its raw byte form and if you inspect ``somevar`` and
see a 32-byte hex value, this is just ``"stringliteral"`` in hex.

The type ``bytes`` is similar, only that it can change its length.

Finally, ``string`` is basically identical to ``bytes`` only that it is assumed
to hold the UTF-8 encoding of a real string. Since ``string`` stores the
data in UTF-8 encoding it is quite expensive to compute the number of
characters in the string (the encoding of some characters takes more
than a single byte). Because of that, ``string s; s.length`` is not yet
supported and not even index access ``s[2]``. But if you want to access
the low-level byte encoding of the string, you can use
``bytes(s).length`` and ``bytes(s)[2]`` which will result in the number
of bytes in the UTF-8 encoding of the string (not the number of
characters) and the second byte (not character) of the UTF-8 encoded
string, respectively.


Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract?
=====================================================================================================

Expand Down Expand Up @@ -457,51 +363,6 @@ to create an independent copy of the storage value in memory
``h(x)`` successfully modifies ``x`` because only a reference
and not a copy is passed.

Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why?
==================================================================================================================================================

You can resize a dynamic array in storage (i.e. an array declared at the
contract level) with ``arrayname.length = <some new length>;``. If you get the
"lvalue" error, you are probably doing one of two things wrong.

1. You might be trying to resize an array in "memory", or

2. You might be trying to resize a non-dynamic array.

::

// This will not compile

pragma solidity ^0.4.18;

contract C {
int8[] dynamicStorageArray;
int8[5] fixedStorageArray;

function f() {
int8[] memory memArr; // Case 1
memArr.length++; // illegal

int8[5] storage storageArr = fixedStorageArray; // Case 2
storageArr.length++; // illegal

int8[] storage storageArr2 = dynamicStorageArray;
storageArr2.length++; // legal


}
}

**Important note:** In Solidity, array dimensions are declared backwards from the way you
might be used to declaring them in C or Java, but they are access as in
C or Java.

For example, ``int8[][5] somearray;`` are 5 dynamic ``int8`` arrays.

The reason for this is that ``T[5]`` is always an array of 5 ``T``'s,
no matter whether ``T`` itself is an array or not (this is not the
case in C or Java).

Is it possible to return an array of strings (``string[]``) from a Solidity function?
=====================================================================================

Expand All @@ -528,21 +389,6 @@ https://github.com/ethereum/wiki/wiki/Subtleties

After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.


What does the following strange check do in the Custom Token contract?
======================================================================

::

require((balanceOf[_to] + _value) >= balanceOf[_to]);

Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range.
For ``uint256``, this is ``0`` up to ``2**256 - 1``. If the result of some operation on those numbers
does not fit inside this range, it is truncated. These truncations can have
`serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_, so code like the one
above is necessary to avoid certain attacks.


More Questions?
===============

Expand Down
82 changes: 50 additions & 32 deletions docs/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
Types
*****

Solidity is a statically typed language, which means that the type of each
variable (state and local) needs to be specified.
Solidity provides several elementary types which can be combined to form complex types.
Solidity is a statically typed language, which means that you need to specify the type of each variable (state and local). Solidity provides elementary types which you can combine to form complex types.

In addition, types can interact with each other in expressions containing
operators. For a quick reference of the various operators, see :ref:`order`.
Types can interact in expressions containing operators. For a quick reference of the various operators, see :ref:`order`.

The concept of "undefined" or "null" values do not exist in Solidity. To handle any unexpected values, you should use ``revert("Error message")`` to revert the whole transaction.

.. index:: ! value type, ! type;value

Expand Down Expand Up @@ -52,6 +51,12 @@ Operators:
* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation)
* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder), ``**`` (exponentiation), ``<<`` (left shift), ``>>`` (right shift)

The bit size of an unsigned integer determines its range, for example, for ``uint256``, this is 0 up to 2 :sup:`256` -1. If the result of an operation doesn't fit inside this range, it is truncated. Not taking these truncations into account can have `serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_. For example, the code below checks to see if the balance plus a value is actually greater than or equal to the original value.

::

require((balanceOf[_to] + _value) >= balanceOf[_to]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works for checking overflow for unsigned types, so we should make that clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

@axic axic Sep 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bad idea without explanation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@axic I thought the text above and the example were enough, but added a little more, hopefully I got it right.


Division always truncates (it is just compiled to the ``DIV`` opcode of the EVM), but it does not truncate if both
operators are :ref:`literals<rational_literals>` (or literal expressions).

Expand Down Expand Up @@ -296,16 +301,28 @@ a non-rational number).
uint128 a = 1;
uint128 b = 2.5 + a + 0.5;

.. index:: literal, literal;string, string
.. index:: literal, literal;string, string, string length, string size

String Literals
String Literals and Types
---------------

String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``.
String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes not four. As with integer literals, their type can vary, but they are implicitly convertible from ``bytes1`` through ``bytes32``, if they fit, and to ``bytes`` and to ``string``. For example, with ::

"stringliteral";

The string literal is interpreted in its raw byte form and with certain tools, if you inspect ``somevar``, you see a 32-byte hex value, which is the ``"stringliteral"`` in hex.

String literals support escape characters, such as ``\n`` for line breaks, ``\xNN`` and ``\uNNNN`` for special characters. ``\xNN`` takes a hex value and inserts the appropriate character, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 character.


String Types
^^^^^^^^^^^^

String literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence.
A string literal becomes a ``string`` type when you assign it to a variable, for example::

.. index:: literal, bytes
bytes32 samevar = "stringliteral";

A ``string`` type is basically identical to ``bytes``, but it is assumed to hold the UTF-8 encoding of a real string. Since ``string`` UTF-8 encoding of some characters takes more than a single byte, it is quite expensive to compute the number of characters in the string. Because of this, Solidity does not support length methods ``string s; s.length``, or accessing an item at an array index ``s[2]``. If you want to access the low-level byte encoding of the string, you can use ``bytes(s).length`` and ``bytes(s)[2]`` which returns the number of bytes in the UTF-8 encoding of the string (not the number of characters) and the second byte (not character) of the UTF-8 encoded string, respectively.

Hexadecimal Literals
--------------------
Expand All @@ -321,9 +338,8 @@ Hexademical Literals behave like String Literals and have the same convertibilit
Enums
-----

Enums are one way to create a user-defined type in Solidity. They are explicitly convertible
to and from all integer types but implicit conversion is not allowed. The explicit conversions
check the value ranges at runtime and a failure causes an exception. Enums needs at least one member.
Enums are one way to create a user-defined type in Solidity. They are explicitly convertible to and from all integer types but implicit conversion is not allowed. The explicit conversions check the value ranges at runtime and a failure causes an exception. Enums need at least one member.


::

Expand Down Expand Up @@ -582,10 +598,7 @@ Default data location:
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.
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.

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
Expand Down Expand Up @@ -680,15 +693,27 @@ possible:
It is planned to remove this restriction in the future but currently creates
some complications because of how arrays are passed in the ABI.

You can initialise a statically sized memory array inline using syntax such as ``string[] memory myarray = new string[](4);``. For example::

pragma solidity ^0.4.16;

contract C {
function f() public pure returns (uint8[5]) {
string[4] memory adaArr = ["This", "is", "an", "array"];
return [1, 2, 3, 4, 5];
}
}

You can create and initialise multi-dimensional arrays in the same ways, but filling arrays can cost a lot of gas, so make sure you optimise how you use them.

.. index:: ! array;length, length, push, pop, !array;push, !array;pop

Members
^^^^^^^

**length**:
Arrays have a ``length`` member to hold their number of elements.
Dynamic arrays can be resized in storage (not in memory) by changing the
``.length`` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created.
You can resize dynamic arrays in storage (**not in memory**) by changing the ``.length`` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. 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 can be used to append an element at the end of the array. The function returns the new length.
**pop**:
Expand Down Expand Up @@ -827,22 +852,14 @@ shown in the following example:
}

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.
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.
This restriction is necessary, as the size of the struct has to be finite.
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. 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).
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.
...note::
All the functions above assign a ``struct`` type to a local variable (of the default storage data location). 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``.
You can also directly access the members of the struct without assigning it to a local variable, as in ``campaigns[campaignID].amount = 0``.

.. index:: !mapping

Expand Down Expand Up @@ -984,3 +1001,4 @@ converted to a matching size. This makes alignment and padding explicit::
bytes32(uint256(x)); // pad on the left
bytes32(bytes2(x)); // pad on the right

.. index:: ! type;deduction, ! var