Is it possible to decode data and make assignment to struct members and new variables in one step

Hi guys,

I was trying to decode call data and make assignment to struct members in one call. It’s okay to do it only for struct members, but not okay to mix new variables on the left hand side. See examples below. runTest works fine but runTest2 and runTest3 do not. Can see an error message like this “ParserError: Expected ‘,’, but got identifier for the lines address _asset”. Wondering why and if it’s possible to make it work. Thanks.

// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

contract Tester {

    struct Params {
        address account;
        uint256 value;
    }

    function runTest(bytes calldata data) external pure {
        Params params;
        (
            params.account, 
            params.value
        ) = abi.decode(data, (address,uint256));
    }

    function runTest2(bytes calldata data) external pure {
        Params params;
        (
            params.account,
            address _asset,
            params.value
        ) = abi.decode(data, (address,address,uint256));
    }

    function runTest3(bytes calldata data) external pure {
        Params params;
        (
            address _asset, 
            params.account, 
            params.value
        ) = abi.decode(data, (address,address,uint256));
    }
}

As per the docs, you’re not allowed to mix variable declarations and non-declaration assignments when using tuples, which is why you’re getting the parser error.

function runTest2(bytes calldata data) external pure {
    Params memory params;
    address _asset; // Declare here
    (
        params.account,
        _asset, // Assign here
        params.value
    ) = abi.decode(data, (address,address,uint256));
}

In other words, given the following function:

function values() internal pure {
    return (2, 3);
}

The following is allowed:

uint256 x;
uint256 y;
(x, y) = values();

As well as this:

(uint256 x, uint256 y) = values();

But not this (which is the problem you were running into):

uint256 x;
(x, uint256 y) = values();
1 Like

Thanks for the detailed answer. This is very clear. Just wondering why this is designed this way. Is it possible to allow both declaration and non-declaration assignments using tuples? Sometimes, it is memory efficient and can improve code readability. Thanks.

As to why it was done this way - I have no idea, since it was before my time. Maybe @chriseth or @daniel can chime in. As to whether it’s possible - at the moment, no, but technically I don’t see why it couldn’t be implemented. None the less, the next main thing is generics, and until those are fleshed out and implemented, we’re likely not going to be addressing this (or anything type related), as it would just over complicate things later on.

1 Like

To add to @nikola-matic’s answer, we won’t be allowing this because the current syntax is a bit irregular and we’re trying to move away from such irregularities. If you look at it closer, it’s a really odd construct with arbitrary limitations:

  • It’s a mix between a destructuring expression and a declaration. You cannot do that with other types, e.g. arrays.
  • You can only do it for local variables. Not for state variables or constants at file-level.
  • It can be nested only one level deep. E.g. ((uint x, uint y), uint z) = ((1, 2), 3) is not allowed.

If we allowed using existing variables in it, the mix would become even weirder. We want to go in the opposite direction. We’re going to introduce proper, nameable tuple types in the not-so-distant future and the syntax will likely become (uint, uint) x = (1, 2) instead. And destructuring will become just another type of an expression, usable not just with tuples.

1 Like

Thanks for the explanations. Good to know the boundries Solidity has and the direction Solidity is going.