BudiBadu Logo
Samplebadu

Solidity by Example: Mappings

0.8.x

Master Solidity mappings with this sample code showing hash table-like key-value storage, virtual initialization returning default values for unset keys, nested mapping structures, and the msg.sender global variable for caller identification.

Code

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

contract MappingExample {
    // Mapping from address to uint (e.g., balances)
    mapping(address => uint256) public balances;

    // Nested mapping (address -> (address -> bool))
    // e.g., checking if an address is approved by another
    mapping(address => mapping(address => bool)) public isApproved;

    function updateBalance(uint256 _amount) public {
        balances[msg.sender] = _amount;
    }

    function approve(address _spender) public {
        isApproved[msg.sender][_spender] = true;
    }

    function getBalance(address _user) public view returns (uint256) {
        // Returns 0 if key is not set
        return balances[_user];
    }
}

Explanation

Mappings are Solidity's most frequently used data structure, functioning like hash tables or dictionaries in other languages. They store key-value pairs using the syntax mapping(KeyType => ValueType). Under the hood, mappings use the keccak256 hash function to compute storage locations dynamically, combining the key with the mapping's storage slot. This enables constant-time O(1) lookups regardless of size, making mappings extremely efficient for sparse data.

A unique characteristic of mappings is their virtual initialization. Every possible key is conceptually initialized to exist, mapped to a value whose byte-representation is all zeros. This means you never need to check if a key exists before accessing it. If a key hasn't been explicitly set, you get the default value: 0 for uint, false for bool, or address(0) for addresses. No storage is consumed until you actually assign a value, making mappings gas-efficient for large key spaces.

Mappings cannot be iterated over, and you cannot retrieve a list of all keys or determine the mapping's size. If you need these features, you must implement them yourself by maintaining a separate array of keys or using specialized libraries. This limitation exists because mappings don't store keys explicitly, only the hashed storage locations of values.

Nested mappings are powerful for complex relationships, such as ERC20 token allowances where one address approves another to spend tokens on their behalf. The syntax mapping(address => mapping(address => uint256)) creates a two-dimensional structure. Nested mappings follow the same virtual initialization rules, with each level returning default values for unset keys.

Code Breakdown

6
mapping(address => uint256) creates a hash table associating addresses with numbers.
10
Nested mapping enables two-level lookups for approval mechanisms.
13
msg.sender is a global variable representing the address calling the function.
21-23
Accessing unmapped keys returns default value (0 for uint) without errors.