はじめに
今回はdAppsを練習で実装している際に「storage」と「memory」の扱い方に関して少し戸惑ったので備忘録として残しておきます。
storage
storageはブロックチェーン上に永久に保存される変数。
可変長配列を使用する場合、storageで宣言する必要がある。
関数外で宣言する場合、デフォルトでstorageになっている。
ブロックチェーン上に保存するので、ガス代が高くなる。
memory
コントラクトを実行する場合にのみ保持される変数。
→ブロックチェーン自体に保存されるわけではないため、storageと異なりガス代もかからない。
関数内で宣言する場合にはデフォルトでmemoryになっている。
実装の際に苦労した点
可変長配列を返そうと考えたが、結構苦戦しました。
以下がうまくいかなかったコードです。
function _transferableNFTs(uint256 stage) private view returns (NFTItem[] memory) {
// return用の変数の宣言
NFTItem[] list;
// itemsは状態変数
for (uint256 index = 0; index < items.length; index++) {
if (items[index].stage == stage && !items[index].sold) {
list.push(items[index]);
}
}
return list;
}
memoryであるにもかかわらず配列の宣言の際に初期化を行っていないのでエラーになりました。
そして次に書いたのが以下のコード
function _transferableNFTs(uint256 stage) private view returns (NFTItem[] storage) {
// return用の変数の宣言
NFTItem[] storage list;
// itemsは状態変数
for (uint256 index = 0; index < items.length; index++) {
if (items[index].stage == stage && !items[index].sold) {
list.push(items[index]);
}
}
return list;
}
デフォルトでmemoryになっているので宣言の際に「storage」を追加し、返り値もstorageに変更しました。
しかし、storageを返り値にすることができないためエラーになります。
そして以下のようなコードに変更しました
function _transferableNFTs(uint256 stage) private view returns (NFTItem[] memory) {
// _transferableNFTCountで事前に配列の数を取得する
uint256 count = _transferableNFTCount(stage);
// return用の変数の宣言
NFTItem[] list = new NFTItem[](count);
// 新しい配列用のインデックスの宣言
uint256 currentIndex = 0;
// itemsは状態変数
for (uint256 index = 0; index < items.length; index++) {
if (items[index].stage == stage && !items[index].sold) {
// pushではなく代入にする
list[currentIndex] = items[index];
currentIndex++;
}
}
return list;
}
storageで値を返すことはできないので、memoryの宣言に変更しました。
memoryで宣言する場合、固定長配列になるので事前に配列の長さを定義しておく必要があります。 そのため、宣言前に配列の長さを取得しています。
また、固定長配列の場合は「push」ではなく、代入になるので「currentIndex」というインデックスを別途宣言しています。
これによってmemoryでも実質的に可変長配列を返却できるようになります。
最後に
solidityに可変長配列がなさそうだったので実質的に実装しました。
もし、より効率的な方法があれば教えていただけると幸いです🙇