# Solidityのmemoryとstorageについての備忘録

By [Adachi Tomoki](https://paragraph.com/@adachi-tomoki) · 2022-03-17

---

### mirror初投稿です！

Web3に関心があるのでこれからコーディング等のWeb3に関する知識のアウトプットに利用していこうと思います！

今回は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 NFTItemUnsupported embed;
    
            // 新しい配列用のインデックスの宣言
            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に可変長配列がなさそうだったので実質的に実装しました。

もしより効率的な方法があれば教えていただけると幸いです🙇

---

*Originally published on [Adachi Tomoki](https://paragraph.com/@adachi-tomoki/solidity-memory-storage)*
