Reference Types
Reference types (arrays, bytes, string, structs, mappings) must carry a data location when used in functions (Chapter 9).
Reference types (arrays, bytes, string, structs, mappings) must carry a data location when used in functions (Chapter 9).
8.1 Arrays
Note: T[][5] in Solidity means 5 dynamic arrays of T — the notation is reversed compared with C.
Array slices exist for calldata arrays only: data[4:], data[start:end].
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Arrays {
uint256[] public dynamicArr; // dynamic storage array
uint256[5] public fixedArr; // fixed-size storage array
uint256[][] public nested; // array of dynamic arrays
function storageOps() external {
dynamicArr.push(10); // append
dynamicArr.push(20);
dynamicArr[0] = 11; // index (out of bounds → Panic(0x32))
dynamicArr.pop(); // remove last
uint256 len = dynamicArr.length;
fixedArr[4] = len;
delete dynamicArr; // reset to empty
}
function memoryArrays(uint256 n) external pure returns (uint256[] memory) {
uint256[] memory arr = new uint256[](n); // memory arrays have fixed length once created
for (uint256 i = 0; i < n; i++) arr[i] = i * i;
// arr.push(1); // ERROR: push/pop only for dynamic storage arrays
return arr;
}
function literals() external pure returns (uint8[3] memory) {
return [1, 2, 3]; // array literal: type is uint8[3] memory
}
// Remove e
…8.2 bytes and string
Strings have no built-in indexing, length, or comparison — convert to bytes. Use bytes for arbitrary binary data (more compact than bytes1[]).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Strings {
string public name = "Solidity";
bytes public data = hex"deadbeef";
function lengthOf(string calldata s) external pure returns (uint256) {
return bytes(s).length; // length in BYTES (UTF-8), not characters
}
function concat(string memory a, string memory b) external pure returns (string memory) {
return string.concat(a, " ", b); // 0.8.12+
}
function equal(string memory a, string memory b) external pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b)); // no == for strings
}
function appendByte() external {
data.push(0xff); // bytes supports push/pop in storage
}
function emoji() external pure returns (string memory) {
return unicode"Hello 👋"; // unicode literal
}
}8.3 Structs
A struct cannot contain a member of its own type directly (but can contain a dynamic array or mapping of it).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Todos {
struct Todo {
string text;
bool completed;
uint64 createdAt;
}
Todo[] public todos;
function create(string calldata text) external {
todos.push(Todo(text, false, uint64(block.timestamp))); // positional
// todos.push(Todo({text: text, completed: false, createdAt: 0})); // named
}
function toggle(uint256 i) external {
Todo storage t = todos[i]; // storage pointer: modifies state directly
t.completed = !t.completed;
}
function get(uint256 i) external view returns (string memory text, bool completed) {
Todo memory t = todos[i]; // memory copy
return (t.text, t.completed);
}
}8.4 Mappings
Mapping facts:
Every possible key "exists" and maps to the zero value — you cannot tell "unset" from "set to zero" without an extra flag.
Mappings are not iterable and have no length; track keys in a separate array if you need iteration (or use OpenZeppelin EnumerableMap/EnumerableSet).
Only allowed in storage (or as storage-pointer parameters in internal/library functions).
Key types: value types, bytes, string, contracts, enums, UDVTs. Value types: anything, including mappings and structs.
Value at key k for a mapping at slot p is stored at keccak256(abi.encode(k, p)).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Mappings {
mapping(address => uint256) public balances; // getter balances(addr)
mapping(address owner => mapping(address spender => uint256)) public allowance; // named keys (0.8.18+)
function set(uint256 amount) external { balances[msg.sender] = amount; }
function remove() external { delete balances[msg.sender]; } // resets to 0
function approve(address spender, uint256 amount) external {
allowance[msg.sender][spender] = amount;
}
}8.5 delete
delete x resets x to its zero value. For arrays it sets length to 0 (dynamic) or zeroes all elements (fixed); for structs it resets every member except mappings (mappings inside structs are not cleared).