🎓 A1Learner
🔍
0 of 50 lessons completed

Solidity Data Locations

A data location is the place where a value lives while a contract works with it. Solidity names three locations, and each one behaves in a different way.

A storage reference writes through to state

The local variable ref is marked storage, so it points at the real array. Writing ref[0] changes the stored first item.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract NumberStore {
    uint256[] public numbers;

    constructor() {
        numbers.push(1);
        numbers.push(2);
    }

    function changeFirst() public {
        uint256[] storage ref = numbers;
        ref[0] = 99;
    }

    function first() public view returns (uint256) {
        return numbers[0];
    }
}

A memory copy does not change state

The local variable copy is marked memory, so it holds its own copy of the array. The copy changes to 99 while the stored value stays 1.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract NumberCopy {
    uint256[] public numbers;

    constructor() {
        numbers.push(1);
        numbers.push(2);
    }

    function changedCopyValue() public view returns (uint256) {
        uint256[] memory copy = numbers;
        copy[0] = 99;
        return copy[0];
    }

    function first() public view returns (uint256) {
        return numbers[0];
    }
}

Calldata holds incoming arguments

This external function receives its array in calldata and only reads it. It returns the length of the array that arrived.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract CalldataDemo {
    function countItems(uint256[] calldata items) external pure returns (uint256) {
        return items.length;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract WorkshopNumbers {
    uint256[] public numbers;

    constructor() {
        numbers.push(7);
        numbers.push(8);
    }

    function changeFirstThroughReference() public {
        uint256[] storage ref = numbers;
        ref[0] = 70;
    }

    function changedCopyValue() public view returns (uint256) {
        uint256[] memory copy = numbers;
        copy[0] = 700;
        return copy[0];
    }

    function countItems(uint256[] calldata items) external pure returns (uint256) {
        return items.length;
    }

    function first() public view returns (uint256) {
        return numbers[0];
    }
}

Key points

  • Storage: the lasting data area of a contract, where state variables live.
  • Memory: a temporary working area that exists only while a function runs.
  • Calldata: the read-only area that holds the arguments a call arrives with.
  • Storage reference: a local storage variable points at the real state, so writes through it change the state.
  • Memory copy: a local memory variable holds a copy, so changing it leaves storage untouched.
  • Parameter rule: array and string parameters must name a location, such as memory or calldata.

In this example, the WorkshopNumbers contract keeps an array in storage with 7 and 8. The changeFirstThroughReference function writes 70 through a storage reference, so the stored first item changes. The changedCopyValue function changes a memory copy to 700 and returns it, while the stored first item stays 7. The countItems function reads an incoming calldata array and returns its length.

Note

calldata values can be read but never changed, which suits arguments a function only inspects.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

A function changes a local memory copy of a stored array. What happens to the stored array?