Move コンパイラは、レシーバー構文をサポートしています。これにより、構造体のインスタンスで呼び出すことができるメソッドを定義できます。これは、他のプログラミング言語のメソッド構文に似ています。構造体のフィールドを操作する関数を定義する便利な方法です。

メソッド構文

関数の最初の引数がモジュール内部の構造体である場合、その関数は . 演算子を使用して呼び出すことができます。関数が別のモジュールの構造体を使用する場合、デフォルトではメソッドは構造体に関連付けられません。この場合、関数は標準の関数呼び出し構文を使用して呼び出すことができます。

モジュールがインポートされると、メソッドは自動的に構造体に関連付けられます。

module book::hero {
    /// ヒーローを表す構造体。
    public struct Hero has drop {
        health: u8,
        mana: u8,
    }

    /// 新しいヒーローを作成する。
    public fun new(): Hero { Hero { health: 100, mana: 100 } }

    /// マナを消費して呪文を唱えるメソッド。
    public fun heal_spell(hero: &mut Hero) {
        hero.health = hero.health + 10;
        hero.mana = hero.mana - 10;
    }

    /// ヒーローの体力を返すメソッド。
    public fun health(hero: &Hero): u8 { hero.health }

    /// ヒーローのマナを返すメソッド。
    public fun mana(hero: &Hero): u8 { hero.mana }

    #[test]
    // `Hero` 構造体のメソッドをテストする。
    fun test_methods() {
        let mut hero = new();
        hero.heal_spell();

        assert!(hero.health() == 110, 1);
        assert!(hero.mana() == 90, 2);
    }
}

メソッドエイリアス

複数の構造体とそのメソッドを定義するモジュールの場合、名前の競合を避けたり、構造体により適切な名前のメソッドを提供したりするために、メソッドエイリアスを定義することができます。

エイリアスの構文は以下の通りです:

// ローカルメソッド関連付け
use fun function_path as Type.method_name;

// エクスポートされたエイリアス
public use fun function_path as Type.method_name;

パブリックエイリアスは、同じモジュールで定義された構造体に対してのみ許可されます。構造体が別のモジュールで定義されている場合でも、エイリアスを作成することはできますが、パブリックにすることはできません。

以下の例では、hero モジュールを変更し、Villain という別の型を追加しました。HeroVillain は似たようなフィールド名とメソッドを持っています。名前の競合を避けるために、メソッドにはそれぞれ hero_villain_ というプレフィックスを付けています。しかし、これらのメソッドのエイリアスを作成することで、プレフィックスなしで構造体のインスタンスで呼び出すことができます。

module book::hero_and_villain {
    /// ヒーローを表す構造体。
    public struct Hero has drop {
        health: u8,
    }

    /// 悪役を表す構造体。
    public struct Villain has drop {
        health: u8,
    }

    /// 新しいHeroを作成する。
    public fun new_hero(): Hero { Hero { health: 100 } }

    /// 新しいVillainを作成する。
    public fun new_villain(): Villain { Villain { health: 100 } }

    // `hero_health` メソッドのエイリアス。
    // モジュールがインポートされると自動的にインポートされる。
    public use fun hero_health as Hero.health;

    public fun hero_health(hero: &Hero): u8 { hero.health }

    // `villain_health` メソッドのエイリアス。
    // モジュールがインポートされると自動的にインポートされる。
    public use fun villain_health as Villain.health;

    public fun villain_health(villain: &Villain): u8 { villain.health }

    #[test]
    // `Hero` と `Villain` 構造体のメソッドをテストする。
    fun test_associated_methods() {
        let hero = new_hero();
        assert!(hero.health() == 100, 1);

        let villain = new_villain();
        assert!(villain.health() == 100, 3);
    }
}

ご覧のように、テスト関数では、プレフィックスなしで Hero と Villain のインスタンスに対して health メソッドを呼び出しています。コンパイラは自動的にメソッドを構造体に関連付けます。

外部モジュールのメソッドのエイリアス作成

現在のモジュールの構造体に、別のモジュールで定義された関数を関連付けることも可能です。同じアプローチに従って、別のモジュールで定義されたメソッドのエイリアスを作成できます。標準ライブラリbcs::to_bytes メソッドを使用し、Hero 構造体に関連付けてみましょう。これにより、Hero 構造体をバイトのベクトルにシリアライズできるようになります。

// TODO: より良い例(外部モジュール...)
module book::hero_to_bytes {
    // `bcs::to_bytes` メソッドのエイリアス。
    // インポートされたエイリアスはモジュールの先頭で定義する必要がある。
    // public use fun bcs::to_bytes as Hero.to_bytes;

    /// ヒーローを表す構造体。
    public struct Hero has drop {
        health: u8,
        mana: u8,
    }

    /// 新しいヒーローを作成する。
    public fun new(): Hero { Hero { health: 100, mana: 100 } }

    #[test]
    // `Hero` 構造体のメソッドをテストする。
    fun test_hero_serialize() {
        // let mut hero = new();
        // let serialized = hero.to_bytes();
        // assert!(serialized.length() == 3, 1);
    }
}

参考文献