模块和脚本

Move 有两种不同类型的程序:模块(Module)脚本(Script)。模块是定义结构类型以及对这些类型进行操作的函数的库。结构类型定义了 Move 的全局存储的模式,模块函数定义了更新存储的规则。模块本身也存储在全局存储中。脚本是可执行文件的入口点,类似于传统语言中的主函数 main。脚本通常调用已发布模块的函数来更新全局存储。脚本是临时代码片段,不会发布在全局存储中。

一个 Move 源文件(或编译单元)可能包含多个模块和脚本。然而,发布模块或执行脚本都是独立的虚拟机(VM)操作。

语法

脚本

脚本具有以下结构:

script {
    <use>*
    <constants>*
    fun <identifier><[type parameters: constraint]*>([identifier: type]*) <function_body>
}

一个 script 块必须以它的所有 use 声明开头,然后是常量(constant)声明,最后是主函数声明。主函数的名称可以是任意的(也就是说,它不一定命名为 main),它是脚本块中唯一的函数,可以有任意数量的参数,并且不能有返回值。下面是每个组件的示例:

script {
    // 导入在命名账户地址 std 上发布的 debug 模块。
    use std::debug;

    const ONE: u64 = 1;

    fun main(x: u64) {
        let sum = x + ONE;
        debug::print(&sum)
    }
}

脚本(Script)的功能非常有限 —— 它们不能声明友元(friend)、结构类型或访问全局存储。他们的主要作用主要是调用模块函数

模块

模块具有以下结构:

module <address>::<identifier> {
    (<use> | <friend> | <type> | <function> | <constant>)*
}

其中 <address> 是一个有效的命名或字面量地址

例子:

module 0x42::test {
    struct Example has copy, drop { i: u64 }

    use std::debug;
    friend 0x42::another_test;

    const ONE: u64 = 1;

    public fun print(x: u64) {
        let sum = x + ONE;
        let example = Example { i: sum };
        debug::print(&sum)
    }
}

module 0x42::test 这部分指定模块 test 将在全局存储账户地址 0x42 下发布。

模块也可以使用命名地址来声明,例如:

module test_addr::test {
    struct Example has copy, drop { a: address }

    use std::debug;
    friend test_addr::another_test;

    public fun print() {
        let example = Example { a: @test_addr };
        debug::print(&example)
    }
}

因为命名地址只存在于源语言级别和编译期间,所以命名地址将在字节码级别彻底替换它们的值。例如,如果我们有以下代码:

script {
    fun example() {
        my_addr::m::foo(@my_addr);
    }
}

我们在把 my_addr 设置为 0xC0FFEE 的情况下编译它,那么它在操作上等同于以下内容:

script {
    fun example() {
        0xC0FFEE::m::foo(@0xC0FFEE);
    }
}

然而,在源代码级别,这些是不等价的 —— 函数 m::foo 必须通过 my_addr 命名地址来访问,而不是通过分配给该地址的数值来访问。

模块名称可以以字母 az 或字母 AZ 开头。在第一个字符之后,模块名可以包含下划线 _、字母 az、字母 AZ 或数字 09

module my_module {}
module foo_bar_42 {}

通常,模块名称以小写字母开头。名为 my_module 的模块应该存储在名为 my_module.move 的源文件中。

module 块内的所有元素都可以按任意顺序出现。从根本上说,模块是类型(type)函数(function)的集合。use 关键字用来从其他模块导入类型。friend 关键字指定一个可信的模块列表。const 关键字定义了可以在模块函数中使用的私有常量。