DEV Community

Cover image for How to Solve "Struct Containing a (Nested) Mapping Cannot be Constructed" in Solidity
Nassim Dehouche
Nassim Dehouche

Posted on

How to Solve "Struct Containing a (Nested) Mapping Cannot be Constructed" in Solidity

This compilation error is encountered when you try to instantiate a struct that contains a mapping as an attribute. I needed this data structure to write this contract to allow patients to license the datasets resulting from their clinical trials, as part of an Algovera grant.

This type of one-to-many data structure can be useful, for instance, to encode a marketplace for time-limited licenses to some content. In addition to the content to be licensed, the struct would need to map licensees' addresses with a timestamp for the start of their license, so that their access can be controlled.

struct content{
address payable owner;
bytes32 hash;
mapping(address => uint) licenses;
}
mapping(address => content[]) public contents;

Visually it would look like this:
Image description

The "struct containing a (nested) mapping cannot be constructed" error would appear when trying to instantiate the struct, the usual way, with a recent compiler version. Like this, for instance:

function submitContent(bytes32 _hash) public payable
returns(uint _id)
{
_id=contents[msg.sender].length;
contents[msg.sender].push(content({
owner: payable(msg.sender),
hash:_hash
return(_id);
}));
}

Indeed, since version 0.7.0, structs or arrays that contain a mapping can only be used in storage, so Solidity complains because variables in an instantiation function would be in memory by default.
You can read about this change in the Solidity documentaiton. The relevant part is under "Removal of Unused or Unsafe Features":
Image description

I wanted to write this short note because some top results when Googling this error are either outdated, or to the extent of "just don't use a mapping in a struct bro". A struct containing a mapping cannot be equivalently modeled with a separate mapping; reading and writing would require much more computation.

The workaround is quite simple, we just have to declare the struct in storage before we instantiate a pointer to it. Our submitContent() function would have to look like this:
function submitContent(bytes32 _hash) public payable
returns(uint _id)
{
_id= contents[msg.sender].length;
content[] storage c = contents[msg.sender];
c.push();
c[_id].owner = payable(msg.sender);
d[_id].hash=_hash;
return _id;
}

Top comments (0)