[Solidityのはじめ方] Part6: 変数と定数(state variables, constant)

Solidityの学習、順調に進んでいますか?😊 今回は、スマートコントラクトの心臓部とも言える「変数」と「定数」について学んでいきましょう。これらを理解することで、コントラクト内でデータをどのように保存し、操作するかが明確になります。

Solidityには、大きく分けて状態変数 (State Variables)ローカル変数 (Local Variables) があります。状態変数はブロックチェーン上に永続的にデータを記録し、ローカル変数は関数実行中の一時的なデータ保持に使われます。また、値が変わらないデータには定数 (Constants)イミュータブル変数 (Immutable Variables) を使うことで、コードの意図を明確にし、ガス代の節約にも繋がりますよ⛽️。


Warning: Undefined array key “is_admin” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 113

Warning: Undefined array key “is_category_top” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 118

Warning: Undefined array key “is_top” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 124

💾 状態変数 (State Variables)

状態変数は、コントラクトの状態をブロックチェーン上に永続的に保存するための変数です。コントラクトがデプロイされた後も、トランザクションを通じてこれらの変数の値を変更できます。コントラクトの「記憶」を担当する部分と考えてください。

宣言はコントラクトの直下(関数の中ではない場所)で行います。基本的な形式は以下の通りです。


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleStorage {
    // 状態変数の宣言例
    uint public storedData; // 符号なし整数型 (public)
    address internal owner; // アドレス型 (internal)
    string private secretMessage; // 文字列型 (private)

    // ... 関数の定義など ...
}
        

状態変数にはアクセス修飾子(`public`, `internal`, `private`)を指定できます。

  • public: コントラクト内部からも外部からもアクセス可能です。`public`を指定すると、Solidityコンパイラが自動的にその変数の値を読み取るためのゲッター関数を生成してくれます。これは非常に便利ですね!✨
  • internal: そのコントラクト内部、およびそのコントラクトを継承したコントラクトからのみアクセス可能です。デフォルトのアクセス修飾子です。
  • private: そのコントラクト内部からのみアクセス可能で、継承したコントラクトからもアクセスできません。

状態変数はブロックチェーンストレージに書き込まれるため、その読み書きにはガス代がかかります。

⚙️ ローカル変数 (Local Variables)

ローカル変数は、関数内でのみ宣言され、その関数の実行が終了すると消滅する一時的な変数です。計算の途中結果を保持したり、関数の引数として受け取った値を加工したりする際に使用します。

宣言は関数の中で行います。


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Calculator {
    function add(uint _a, uint _b) public pure returns (uint) {
        // ローカル変数の宣言と初期化
        uint result = _a + _b; // result はこの関数内でのみ有効
        return result;
    }

    function complexCalculation(uint _startValue) public pure returns (uint) {
        uint intermediateResult = _startValue * 2; // ローカル変数
        intermediateResult = intermediateResult + 5; // ローカル変数
        // ... さらに計算 ...
        return intermediateResult;
    }
}
        

基本型のローカル変数は通常、メモリ(具体的にはスタック)上に配置され、状態変数のようにブロックチェーンストレージを使用しないため、ガス代が非常に安価(または無料)です。ただし、配列や構造体などの参照型のローカル変数を扱う際には、`storage` または `memory` というキーワードでデータがどこに存在するかを明示する必要があります(これは後のステップ「ストレージとメモリの違い」で詳しく学びます)。

🧱 定数 (Constants)

定数は、一度値を設定したら二度と変更できない変数のことです。`constant` キーワードを使って宣言します。

定数の値は、コンパイル時に確定している必要があります。つまり、コードを書いている時点でその値が決まっていなければなりません。例えば、数学的な定数(円周率など、ただしSolidityでは浮動小数点数は扱いに注意が必要)、コントラクト固有の固定パラメータ(最大発行数など)に使われます。


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyConstants {
    // 定数の宣言 (コンパイル時に値が確定)
    uint public constant MAX_SUPPLY = 10000;
    address public constant TOKEN_CONTRACT_ADDRESS = 0xAbC...DeF; // 例: 特定のトークンアドレス
    string public constant GREETING = "Hello, Solidity!";

    // 定数は計算式でも定義可能 (コンパイル時に計算される)
    uint public constant SECONDS_PER_DAY = 24 * 60 * 60;
}
        

定数を使うメリットは以下の通りです。

  • ガス代の節約: 定数の値は、コンパイル時にコード内の参照箇所に直接埋め込まれます。そのため、実行時にストレージから値を読み出す必要がなく、ガス代がかかりません。
  • コードの可読性向上: マジックナンバー(コード中に突然現れる具体的な数値)の代わりに意味のある名前の定数を使うことで、コードが読みやすくなります。
  • 不変性の保証: 値が変更されないことをコンパイラレベルで保証します。

慣習として、定数名はすべて大文字で、単語間をアンダースコア `_` で区切るスネークケース(例: `MAX_SUPPLY`)で命名されることが多いです。

🔒 イミュータブル変数 (Immutable Variables)

イミュータブル変数も定数と同様に、一度値を設定したら変更できませんが、`constant` とは異なり、デプロイ時(コンストラクタ実行時)に一度だけ値を設定することができます。`immutable` キーワードを使って宣言します。

これは、コントラクトをデプロイする際に決定される値(例えば、コントラクトのオーナーアドレスや、外部コントラクトのアドレスなど)を安全かつ効率的に格納するのに役立ちます。


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyImmutable {
    // イミュータブル変数の宣言
    address public immutable owner;
    uint public immutable creationTime;
    address public immutable tokenAddress;

    constructor(address _tokenAddress) {
        // コンストラクタ内で一度だけ値を設定できる
        owner = msg.sender; // デプロイした人のアドレス
        creationTime = block.timestamp; // デプロイ時のタイムスタンプ
        tokenAddress = _tokenAddress; // デプロイ時に指定されたアドレス
    }

    // コンストラクタ以外で immutable 変数に代入しようとするとエラーになる
    // function changeOwner(address _newOwner) public {
    //     owner = _newOwner; // Error! Cannot assign to an immutable variable.
    // }
}
        

イミュータブル変数の特徴:

  • デプロイ時に値設定: コンストラクタ内でのみ値を設定できます。
  • ガス代の節約: 定数ほどではありませんが、状態変数よりはるかにガス効率が良いです。値はデプロイ時に確定し、実行時には定数のように扱われますが、値を格納するために32バイトの領域が確保されます。
  • 柔軟性: デプロイごとに異なる値を設定したい場合に便利です(例: テストネットとメインネットで異なる外部コントラクトアドレスを設定するなど)。

`immutable` 変数も、慣習的に先頭に `i_` を付ける(例: `i_owner`)か、定数と同じく大文字スネークケースで命名されることがあります。プロジェクトのコーディング規約に従いましょう。

💡 まとめ:変数と定数の使い分け

それぞれの特徴を理解し、適切な場面で使い分けることが重要です。

種類 宣言場所 永続性 値の変更 値の設定タイミング ガス効率 主な用途
状態変数
(State Variable)
コントラクト直下 永続 (ブロックチェーン上) 可能 いつでも (トランザクション経由) 低い (読み書きにガス要) コントラクトの状態、ユーザーデータなど
ローカル変数
(Local Variable)
関数内 一時的 (関数実行中のみ) 可能 関数実行中 高い (スタック/メモリ使用) 一時的な計算、関数の引数処理
定数
(Constant)
コントラクト直下 or ファイルレベル 永続 (コードに埋込) 不可 コンパイル時 非常に高い (ガス不要) 固定されたパラメータ、数学定数
イミュータブル変数
(Immutable)
コントラクト直下 永続 (コードに埋込) 不可 デプロイ時 (コンストラクタ内) 高い (定数よりは少し劣る) オーナーアドレス、デプロイ時設定パラメータ

これらの変数の種類と特性をしっかり理解することは、安全で効率的なスマートコントラクトを開発するための基礎となります。特にガス代はユーザーの負担に直結するため、`constant` や `immutable` を活用して最適化を意識しましょう! 💪

次のステップでは、Solidityの関数の定義方法について詳しく見ていきます。お楽しみに!