Solidityメモ: structの初期化
struct(構造体)の初期化でちょっとハマったのでメモ。 (Solidity 0.8.10でテストしています。)
Solidity documentation - Types - Structs
structのメンバにstringの配列がある場合 (基本の例)
まず簡単な例として、Itemというstructを定義しました。メンバはstring型のnameとstringの配列のdataを持ちます。
add1,add2,add3という関数を作り、いずれもItemを作成して配列(items)に追加しています。 メンバにstringの配列があるので、これをどう初期化するかがポイントです。
add1
: dataに長さ0の配列を指定してItemを作成していますadd2
: dataは指定していませんが、空配列で初期化されますadd3
: 先にarrayにpush()してからnameを指定します (これもdataは空配列になります)
struct Item { string name; string[] data; } Item[] public items; function add1() public { items.push(Item("add1", new string[](0))); } function add2() public { Item memory item; item.name = "add2"; items.push(item); } function add3() public { Item storage item = items.push(); // ※ item.name = "add3"; }
(※配列にはpush(x)とpush()のメソッドがありますが、push()の戻り値は新しく追加した要素になります。)
structを作成するにもいろいろな方法があります。と、まあ、ここまでは、いいのですが、、、。
structのメンバにstructの配列がある場合
Childというstructを定義して、stringのかわりにChildの配列をメンバにしてみます。
string[]
からChild[]
に変えただけですが、add1とadd2がコンパイルエラーになりました。
やっかいなことにVSCodeで書いている時にはエラーとならず、コンパイルしてはじめてエラー表示されます。 エラーメッセージに「not yet supported」とあるので、まだ実装されてないけど、そのうち実装される機能ということでしょうか。
add3のように直接storageで作成すればいけるようです。
struct Child { int256 id; string name; } struct Item { string text; Child[] data; // ← string[] data; から変更 } Item[] public items; function add1() public { // ↓コンパイルエラー UnimplementedFeatureError: Copying of type struct Child memory[] memory to storage not yet supported. items.push(Item("add1", new Child[](0))); } function add2() public { // ↓コンパイルエラー UnimplementedFeatureError: Copying of type struct Child memory[] memory to storage not yet supported. Item memory item; item.text = "add2"; items.push(item); } function add3() public { // これはOK Item storage item = items.push(); item.text = "add3"; }
structのメンバにmappingがある場合
次は配列ではなくmappingを含む場合ですが、こちらも add1とadd2がエラーになります。 今回はVSCodeで書いているときにもエラー表示が出ます。 mappingを含むstructは、memoryとして作成できないようです。
実は、add1もadd2もSolidityの古いバージョンではエラーにならなかったのですが、 Solidity 0.7.0以後はエラーが出るようになりました。 (そのため、古い記事にはadd1やadd2のような書き方をしているものもあります。)
mappingを含む場合も add3なら問題ありません。
struct Item { string text; mapping(uint256 => uint256) data; } Item[] public items; function add1() public { // ↓エラー Struct containing a (nested) mapping cannot be constructed. items.push(Item("add1")); } function add2() public { // ↓エラー Type struct Item is only valid in storage because it contains a (nested) mapping. Item memory item; item.text = "add2"; items.push(item); } function add3() public { // これはOK Item storage item = items.push(); item.text = "add3"; }
おわりに
Solidityややこしいですね😖
内容に間違いや、もっとこう書けるよという点がありましたら、ぜひ教えてください。