结构体方法

Move 编译器支持_接收者语法_,允许在结构体实例上定义可调用的方法。这类似于其他编程语言中的方法语法。这是一种方便的方式,可以在结构体的字段上定义操作。

方法语法

如果函数的第一个参数是模块内部的结构体,则可以使用 . 运算符调用该函数。如果函数使用另一个模块中的结构体,则默认不会将方法与结构体关联起来。在这种情况下,可以使用标准的函数调用语法来调用该函数。

当导入一个模块时,方法会自动与结构体关联起来。

module book::hero {
    /// A struct representing a hero.
    public struct Hero has drop {
        health: u8,
        mana: u8,
    }

    /// Create a new Hero.
    public fun new(): Hero { Hero { health: 100, mana: 100 } }

    /// A method which casts a spell, consuming mana.
    public fun heal_spell(hero: &mut Hero) {
        hero.health = hero.health + 10;
        hero.mana = hero.mana - 10;
    }

    /// A method which returns the health of the hero.
    public fun health(hero: &Hero): u8 { hero.health }

    /// A method which returns the mana of the hero.
    public fun mana(hero: &Hero): u8 { hero.mana }

    #[test]
    // Test the methods of the `Hero` struct.
    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 模块,并添加了另一种类型 - VillainHeroVillain 都具有类似的字段名称和方法。为了避免名称冲突,我们为这些方法添加了前缀 hero_villain_。但是,我们可以为这些方法创建别名,以便在结构体实例上调用时不需要前缀。

module book::hero_and_villain {
    /// A struct representing a hero.
    public struct Hero has drop {
        health: u8,
    }

    /// A struct representing a villain.
    public struct Villain has drop {
        health: u8,
    }

    /// Create a new Hero.
    public fun new_hero(): Hero { Hero { health: 100 } }

    /// Create a new Villain.
    public fun new_villain(): Villain { Villain { health: 100 } }

    // Alias for the `hero_health` method. Will be imported automatically when
    // the module is imported.
    public use fun hero_health as Hero.health;

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

    // Alias for the `villain_health` method. Will be imported automatically
    // when the module is imported.
    public use fun villain_health as Villain.health;

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

    #[test]
    // Test the methods of the `Hero` and `Villain` structs.
    fun test_associated_methods() {
        let hero = new_hero();
        assert!(hero.health() == 100, 1);

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

正如你所看到的,在测试函数中,我们在 HeroVillain 的实例上调用了 health 方法,而不使用前缀。编译器将自动将方法与结构体关联起来。

别名一个外部模块的方法

还可以将在另一个模块中定义的函数与当前模块的结构体关联起来。按照相同的方法,我们可以为在另一个模块中定义的方法创建别名。让我们使用标准库中的 bcs::to_bytes 方法,并将其与 Hero 结构体关联起来。这将允许将 Hero 结构体序列化为字节向量。

// TODO: better example (external module...)
module book::hero_to_bytes {
    // Alias for the `bcs::to_bytes` method. Imported aliases should be defined
    // in the top of the module.
    // public use fun bcs::to_bytes as Hero.to_bytes;

    /// A struct representing a hero.
    public struct Hero has drop {
        health: u8,
        mana: u8,
    }

    /// Create a new Hero.
    public fun new(): Hero { Hero { health: 100, mana: 100 } }

    #[test]
    // Test the methods of the `Hero` struct.
    fun test_hero_serialize() {
        // let mut hero = new();
        // let serialized = hero.to_bytes();
        // assert!(serialized.length() == 3, 1);
    }
}

进一步阅读