How To Return A Mapping In Solidity and Web3?

Someone in our Facebook Group asked how to return a Mapping in Solidity. It's not possible. But can you eventually return a struct with a mapping? Would that work?

I will give you a spoiler, the short answer is: you can't and it makes no sense. But there are work-arounds.

Let's start with something simple.

Take this sample code, where someone wants to return a mapping:

pragma solidity ^0.5.0;

contract ReturnMapping {
    mapping(address => uint) myMapping;
    
    //this won't work:
    /*
    function returnMapping() public view returns (mapping(address => uint)) {
        return myMapping;
    }
    */
}

If you add this function in Remix it will lead to an error. But how to return the values of the mapping?

Ideally you'll just return the value of a specific mapping key:

pragma solidity ^0.5.0;

contract ReturnMapping {
    mapping(address => uint) myMapping;
    
    //ideal solution
    function returnMappingValue(address _key) public view returns (uint) {
        return myMapping[_key];
    }  
}

But we all know that this is often not possible in real life scenarios. Probably you'll end up with a mapping of structs of mappings (of more mappings). Can you return the whole thing or just one level down (so, the struct with a mapping)?

Let's make our code a bit more complex:

pragma solidity ^0.5.0;

contract ReturnArrayAndMapping {
    struct SomeStruct {
        uint sizeOfMapping;
        mapping(uint => uint) myMappingInStruct;
    }
    
    SomeStruct myVariable;
    
    function addValue(uint _someUint) public {
        myVariable.myMappingInStruct[myVariable.sizeOfMapping++] = _someUint;
    }
}

Could you return all the values of your mapping? All in one function.

Yes, you can. But it's not good.

Let's consider this function:

    function getMappingValue() public view returns (uint[] memory) {
        uint[] memory memoryArray = new uint[](myVariable.sizeOfMapping);
        for(uint i = 0; i < myVariable.sizeOfMapping; i++) {
            memoryArray[i] = myVariable.myMappingInStruct[i];
        }
        return memoryArray;
    }

This function would give you an array of the size of the mapping (stored in SomeStruct->sizeOfMapping which we increment every time we add a value in "addValue(...)".

In an ideal world we could use this function. But on Ethereum there are Gas-Costs attached to every operation. If your mapping scales with the amount of users then at some point you will most likely run into Out-Of-Gas-Exceptions.

This is something I am also covering in my blog post about What exactly is the Gas Limit and the Gas Price in Ethereum.

So, it's always better to get just the final value from outside. Also, just use the smart contract for what it really needs to hold. Not more. A Smart Contract is not a substitute for a normal database/datastore, it's a specific tool to primarily solve the double spending problem without a super-admin having access to the data.

If you want to know more how these things are working with smart contracts, checkout my thorough Cryptocurrency and Blockchain course. It covers everything from A-Z in 6 projects.

If you have any questions, let me know in the comments!