paint-brush
How to Solve "Struct Containing a (Nested) Mapping Cannot be Constructed" in Solidityby@ndehouche
5,077 reads
5,077 reads

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

by Nassim DehoucheMarch 17th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

A compilation error is encountered when you try to instantiate a struct that contains a mapping as an attribute. This type of one-to-many data structure can be useful, for instance, to encode a marketplace for time-limited licenses to some content. But this type of structure cannot be equivalently modeled with a separate mapping for licenses. The workaround is quite simple, we just have to declare the struct in storage before we instantiate a pointer to it.
featured image - How to Solve "Struct Containing a (Nested) Mapping Cannot be Constructed" in Solidity
Nassim Dehouche HackerNoon profile picture

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;

}


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".


However, this type of structure cannot be equivalently modeled with a separate mapping for licenses; 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);

c[_id].hash=_hash;

return _id;

}