🤔
いつmemoryを使い、いつcalldataを使うのか?【Solidity】
memoryとcalldataの使い分け
コーディングしてると良く使い分けに悩むmemory
とcalldata
をまとめてみました
Solidityの5つの記録領域
Solidityは記憶領域が5つあり、これらを用いてデータのやり取りを行う。
- memory
- storage
- calldata
- stack
- returndata
memoryとcalldataの公式ドキュメント
memory
memory から memory への割り当てでは参照のみ作られます。つまり、memory変数への変化は同じデータを参照している他のmemory変数からも可視であるということです。
calldata
Calldataは外部コントラクトのファンクションの参照型のパラメータとして要求される場合にのみ有効です。Calldataは修正不可で、ファンクションの引数が保存される非永続的なエリアで、基本的にはmemoryの様に振舞います。
memoryとcalldataの自分的な理解
memory
- 一時的な保存領域
- 関数の引数と関数内の変数に割り当て可能
- 指定できる型に条件なし
- ミュータブル(上書き可能)
calldata
- 一時的な保存領域
- 関数の引数のみに割り当て可能
- 指定できる型は配列、構造体、マッピング
- イミュータブル(上書き不可)
memoryとcalldataの具体的なコーディング
memory
function test (uint256[] memory input) public returns (uint256 memory) {
input[0] = 1; //memoryなので変更可能
return input;
}
calldata
function test (uint256[] calldata input) public returns (uint256 calldata) {
//calldataなので変更不可
return input;
}
現状の自分の使い分け
- 構造体、配列、マッピング変数で関数内で書き換えが起こらない場合は
calldata
- 構造体、配列、マッピング変数で関数内で書き換えが必要な場合は
memory
- それ以外は全て
memory
参考文献
When should I use calldata and when should I use memory?
Solidityの公式ドキュメント
Discussion
function test (uint256[] calldata input) public returns (uint256 calldata)
について0.8.17の公式ドキュメントを見てみると、0.6.9からcalldataはexternal以外でも使えるようになったとのことですが、publicにしてもそれをコントラクト内の他の関数から呼んだらmemory->calldataのimplicit conversionでエラーがでるのでexternalにしておくべきだと思いました。
ついでに、返り値の型をuint256[]にするのと、pure修飾子をつけるといいと思います。