21

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.

Sang-hoon Shin
  • 425
  • 2
  • 4
  • 9
  • You cannot create a mapping (or a struct containing one) in memory. – goodvibration Sep 14 '20 at 16:24
  • @goodvibration I've also tried a storage and just 'Request newRequest'. But it throws the same error – Sang-hoon Shin Sep 14 '20 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. – goodvibration Sep 14 '20 at 16:25
  • 1
    Storage is for existing items, not for new ones! – goodvibration Sep 14 '20 at 16:25
  • Aha, then how can I create my request with a given variable (in this case, description)? – Sang-hoon Shin Sep 14 '20 at 16:27
  • Like I said - first of all, there is nothing in your code which suggests that you even need this structure to begin with. So either explain why exactly you want it, or just get rid of it altogether. Second, even assuming you explain why you need it, you still need to explain why you need the mapping inside it. Because if you don't, then you can (and should) simply declare the mapping outside the structure and not inside it. Please fix your question to make some sense! – goodvibration Sep 14 '20 at 16:35
  • It is because I need to check whether each of the requests has voted or not. I want to build a voting function in my smart contract. So I made a mapping(address => bool) to check if the address from one specific request is true or not. – Sang-hoon Shin Sep 14 '20 at 20:12
  • 2
    @goodvibration This was actually working before the solidity version 7.0 , when push was used – alper Oct 04 '20 at 12:52

6 Answers6

16

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;

    }

vijay patne
  • 161
  • 1
  • 3
  • With this, I got error: Type Error: Data location can only be specified for array, struct or mapping types, but "storage" was given. – VincentHuang Nov 21 '21 at 03:13
  • What about the approvals mapping in the Request struct? How can you update that? – MehmedB Sep 01 '22 at 07:42
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

hrkrshnn
  • 426
  • 3
  • 9
5

This should be enough:

function createRequest(string memory description) public onlyManager {
    Request storage newRequest = requests.push();
    newRequest.description = description;
}
alper
  • 8,395
  • 11
  • 63
  • 152
user1506104
  • 198
  • 1
  • 6
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.
mn.
  • 141
  • 5
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

Ayan
  • 121
  • 3
0

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