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).