22

My version of solc:

"solc": "^0.7.1",

When I try to construct a struct that contains mapping, I got this error: "Struct containing a (nested) mapping cannot be constructed"

This is my solidity code.

Campaign.sol

pragma solidity >=0.5.0;

contract Campaign {
    struct Request {
        string description;

        // I declared a mapping inside of a struct.
        mapping(address => bool) approvals;

    }
    constructor(uint256 minimum, address creator) {
        ...
    }

    function createRequest(string memory description) public onlyManager {
        Request memory newRequest = Request({ 

            // Here the compiler gives me an error

            description : description

        });
    }
    ...

It says

Struct containing a (nested) mapping cannot be constructed.

Please help me.

8
  • You cannot create a mapping (or a struct containing one) in memory. Commented Sep 14, 2020 at 16:24
  • @goodvibration I've also tried a storage and just 'Request newRequest'. But it throws the same error Commented Sep 14, 2020 at 16:25
  • Since nothing in your code suggests that you even need this struct, the easiest solution would be to simply get rid of it. Commented Sep 14, 2020 at 16:25
  • 1
    Storage is for existing items, not for new ones! Commented Sep 14, 2020 at 16:25
  • 2
    @goodvibration This was actually working before the solidity version 7.0 , when push was used
    – alper
    Commented Oct 4, 2020 at 12:52

6 Answers 6

15

The problem is in the construction, i.e., Request({ description: description }) Therefore changing the location of newRequest from memory to storage will not help.

If you really want to make it work, create a state variable, say, mapping (uint => Request) requests. Now, inside your function, you can write Request storage newRequest = requests[index] where index will get incremented later.

For a reference, see: docs

5
  • Thank you very much! Then is there any way to put a specific address (which is from Request struct) to the key(instead of uint) of a mapping? Commented Sep 14, 2020 at 20:06
  • 1
    You can change it to mapping(address => Request)
    – hrkrshnn
    Commented Sep 17, 2020 at 8:33
  • Thank you for yout help. I'll try. Commented Sep 17, 2020 at 18:53
  • Where do you set the mapping for approvals? @hrkrshnn
    – alper
    Commented Oct 4, 2020 at 14:40
  • @alper you don't really set the mapping for approvals. If you have defined a local struct inside a function, then it has to be a pointer to a storage struct.
    – hrkrshnn
    Commented Oct 5, 2020 at 7:27
17

from 0.7.0 do like below:

 struct Request{
            string description;
            uint value;
            address recipient;
            bool complete;
            uint approvalsCount;
            mapping(address => bool) approvals;
        }
        
    uint numRequests;
    mapping (uint => Request) requests;
    
    function createRequest (string memory description, uint value,
            address recipient) public{
                Request storage r = requests[numRequests++];
                r.description = description;
                r.value = value;
                r.recipient = recipient;
                r.complete = false;
                r.approvalsCount = 0;
            
        }
2
  • With this, I got error: Type Error: Data location can only be specified for array, struct or mapping types, but "storage" was given. Commented Nov 21, 2021 at 3:13
  • What about the approvals mapping in the Request struct? How can you update that?
    – MehmedB
    Commented Sep 1, 2022 at 7:42
5

This should be enough:

function createRequest(string memory description) public onlyManager {
    Request storage newRequest = requests.push();
    newRequest.description = description;
}
2

You cannot create a mapping (or a struct containing a mapping) in memory.

So, convert your code from this:

contract Campaign {
    struct Request {
        string description;
        uint value;
        address recipient;
        bool complete;
        uint approvalCount;
        mapping(address => bool) approvals;
    }

    Request[] public requests;

    function createRequest(string memory description, uint value, address recipient) public restricted {
      Request memory newRequest = Request({
          description: description,
          value: value,
          recipient: recipient,
          complete: false,
          approvalCount: 0
      });
      requests.push(newRequest);
    }
}
    

to this

contract Campaign {
    struct Request {
        string description;
        uint value;
        address recipient;
        bool complete;
        uint approvalCount;
        mapping(address => bool) approvals;
    }
    
    uint numRequests;
    mapping (uint => Request) requests;
    
    function createRequest(string memory description, uint value, address recipient) public restricted {            
        Request storage r = requests[numRequests++];
        r.description = description;
        r.value = value;
        r.recipient = recipient;
        r.complete = false;
        r.approvalCount = 0;
    }
}

Reference

2

I wish to add some background info, to help those transitioning from a managed-memory environment (like the .NET CLR) to the EVM:

  • Usually, you may have instantiated reference types which allocate memory (on the heap). Since solidity EVM memory is more restricted, solidity instead initializes mappings in contract storage instead (instead of in memory). Since instantiating a struct on the stack utilizes memory, this is why this error message pops up when trying to instantiate a solidity struct that contains a mapping.
  • Usually, you may have asked a runtime for new memory (e.g.: using the new keyword in C#). In solidity, this step is abstracted away from the developer & instead the solidity EVM automatically does it for us behind the scenes. Specifically, this occurs when the mapping variable is declared. This is why the code examples on this page don't show a step for memory allocation, & instead just jump straight to setting mapping values.
0

You can't create a struct with a mapping inside. Create a local mapping in your contract at the top level.

2
  • Thank you! How can I connect the mapping with a specific Request? Because I want to check if the request has voted or not (as True or False) Commented Sep 14, 2020 at 20:09
  • Use a common identifier. You keep a mapping with ids and a mapping with ids to structs Commented Sep 15, 2020 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.