Solidity – Passing Struct Array Element as Function Argument: Pass by Value or Reference

solidity

Suppose I have the defined a struct in solidity as below:

struct Organisation {
    string name;
    address ethAddress;
    uint rating;
    string regNumber;
}

I have defined an dynamic array of the above struct as

//  list of all insurers/Organisations

Organisation[] allOrgs;

If i need to pass the element of this array to a internal function which is actually just comparing the value against another value, will it be passed by reference?

function getInsurerEth(string uname) public payable returns(address) {
    for(uint i = 0; i < allOrgs.length; ++ i) {
        if(stringsEqual(allOrgs[i].name, uname)) {
            return allOrgs[i].ethAddress;
        }
    }
}

The function StringsEqual defined as below. I need to know if the first parameter is passed by reference or value. If by value, then I can actually remove the argument attribute storage and make the function pure. Also note that this is a internal function call which will use the jump opcode and also i am not editing the storage element allOrgs[i].name.

    //   internal function to compare strings

function stringsEqual(string storage _a, string memory _b) internal returns (bool) {
    bytes storage a = bytes(_a);
    bytes memory b = bytes(_b);
    if (a.length != b.length)
        return false;
    // @todo unroll this loop
    for (uint i = 0; i < a.length; i ++)
    {
        if (a[i] != b[i])
            return false;
    }
    return true;
}

Best Answer

The first argument of function StringsEqual is passed by reference and not by value because you are explicitly specifying state variable _a in storage.

To declare function StringsEqual as pure you need to pass an argument by value as following -

function stringsEqual(string memory _a, string memory _b) internal pure returns (bool) {
    bytes memory a = bytes(_a);
    bytes memory b = bytes(_b);
    if (a.length != b.length)
        return false;
    // @todo unroll this loop
    for (uint i = 0; i < a.length; i ++)
    {
        if (a[i] != b[i])
            return false;
    }
    return true;

}   

To conclude: when you are explicitly defining storage type of an argument asstorage you are passing an argument to a function by reference.

If you don't specify storage type of a function argument then function arguments are always in memory and you are passing an argument to a function by value.

To add further clarification I tried following :

function stringsEqual(string storage  _a, string  _b) internal  returns (bool) {
    bytes memory a = bytes(_a);
    bytes memory b = bytes(_b);
    if (a.length != b.length)
        return false;
    // @todo unroll this loop
    for (uint i = 0; i < a.length; i ++)
    {
        if (a[i] != b[i])
            return false;
    }
    _a = "xyz";
    _b = "xyz";
    return true;

} 

And I get the following error which confirms that argument is passed by reference :

browser/pass_by_reference_value.sol:198:14: TypeError: Type literal_string "xyz" is not implicitly convertible to expected type string storage pointer.

Related Topic