Skip to content

Latest commit

 

History

History
331 lines (212 loc) · 8.09 KB

03-move-basic-2.md

File metadata and controls

331 lines (212 loc) · 8.09 KB

Move 相关概念:包、模块、脚本及其使用

Move 包(package)概念,简单点理解,例如我们用 IDE 创建一个 Move 目录并包含一个.toml 文件和含sources 目录的源文件,这整个目录就是一个包。而且一个目录下同样可以再创建子目录,子目录中符合上述描述的,也同样是一个包。包的清单都定义在了 Move.toml 文件中。

例如:使用 move-cli 工具创建一个 Move 项目(包),如下

move new myPackage
myPackage
├── sources
└── Move.toml

上面 myPackage 整个目录(工程)就是一个包。sourcesMove.toml 是必须的。

上述是一个精简的包结构,下面这个是比较完整点的包结构。

a_move_package
├── Move.toml      (required)
├── sources        (required)
├── examples       (optional, test & dev mode)
├── scripts        (optional)
├── doc_templates  (optional)
└── tests          (optional, test mode)

更详细见:https://move-language.github.io/move/packages.html

注: Move 包的清单定义在 Move.toml 文件中。Move 模块、Move 脚本都存放在 sources下。

允许 Move 程序员更轻松地重用代码并在项目之间共享。

Move.toml 文件

[package]
# 包名,例如: "myPackage"
name = <string>
# 版本号,例如: "0.0.1"
version = "<uint>.<uint>.<uint>"
# 许可协议,例如: "MIT", "GPL", "Apache 2.0"
license* = <string>
# 作者,例如: ["nangongamo ([email protected])", "web3builder ([email protected])"]
authors* = [<string>]

[addresses]  
# 定义地址(addresses)变量,例如, Std = "_" 或者 Addr = "0x1"
<addr_name> = "_" | "<hex_address>" 

[dependencies] 
# 可以定义一个或多个依赖包。
# 本地依赖
<package_name> = { local = <string>, addr_subst* = { (<string> = (<string> | "<hex_address>"))+ } } 
# git 远程依赖
<package_name> = { git = <git URL地址>, subdir=<git 仓库里包含 .toml 文件的目录路径>, rev=<commit 哈希值>, addr_subst* = { (<string> = (<string> | "<hex_address>"))+ } } 

模块

模块module)是定义在地址(address)下的。所以说模块是发布在特定地址下的打包在一起的一组函数和结构体。

模块以module关键字开头,后面跟随模块名称和大括号,大括号中放置模块内容。

但是我们在写 Move 代码时有两种写法。

  • 写法1

    address <地址变量名> { module <模块名>{ } }

    例如,新建文件 my_module1.move,如下代码:

    address std {
        module math1 {
            public fun add(n1:u64, n2:u64):u64 {
                n1 + n2
            }
    
            #[test]
            fun test_add() {
                let n1:u64 = 1;
                let n2:u64 = 99;
                let n3:u64 = std::math1::add(n1, n2);
                assert!(n3==100u64,1000);
            }
        }
    }
  • 写法2

    module <地址变量名>::<模块名> { }

    例如,新建文件 my_module2.move,如下代码:

    module std::math2 {
        public entry fun add(n1:u64, n2:u64):u64 {
            n1 + n2
        }
    
        #[test]
        fun test_add() {
            let n1:u64 = 1;
            let n2:u64 = 99;
            let n3:u64 = std::math2::add(n1, n2);
            assert!(n3==100u64,1000);
        }
    }

单元测试,执行如下命令:

move test

结果:

INCLUDING DEPENDENCY MoveStdlib
BUILDING 03-move-basic-2
Running Move unit tests
[ PASS    ] 0x1::math2::test_add
[ PASS    ] 0x1::math1::test_add
Test result: OK. Total tests: 2; passed: 2; failed: 0

脚本

Scripts(脚本)按如下结构呈现:

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

脚本块内的函数外是不能定义变量的,只能定义常量,以及如果有模块 ( module ) 导入的话,可以使用 use 关键字导入模块供函数使用。

脚本 ( scripts ) 的作用主要是用于调用模块函数。

其实,脚本的功能是相对有限的,它们不能声明 friendsstruct 类型或访问全局存储。

脚本块中只能有一个函数,函数命名无约束(只是大家都喜欢定义为 main 函数),函数的参数允许没有也可以是多个,但函数是没有返回值的。

示例:

script {

    use std::debug;

    const U64_MAX: u64 = 18446744073709551615;

    fun main(num:u128) {
        let sum = (U64_MAX as u128) + num;
        debug::print(&sum)
    }

}

使用

模块的使用

模块的使用,可以在模块(module)中导入模块,也可以在脚本(script)中导入模块。要访问导入的模块的方法或类型,需要使用::符号。

  • 直接使用,例如:使用 0x1 地址下的标准库 debug 的打印函数(第3行)。

    script {
        fun main(num: u8) {
            0x1::debug::print(&num);
        }
    }
    
  • 使用 use 关键字导入

    use <Address>::<ModuleName>

    <Address> 是模块发布者的地址,<ModuleName> 是模块的名字

    例如:

    script {
    
      use 0x1::debug;
    	
      fun main(num: u8) {
          debug::print(&num);
      }
    }

包的使用

主要是讲下关于 Move 包的依赖。分两种:

  • 一种是依赖远程 git 仓库的 Move 包。
  • 一种是依赖本地 Move 包。

依赖关系都是在 Move 包的 Move.toml 文件中定义的。

依赖远程 git 仓库

这种使用场景较频繁,特别是做项目基本都会依赖一些标准库。例如,Move 官方的标准库。

Move.toml 文件中增加如下配置,即:

[dependencies]
MoveStdlib = { git = "https://github.com/move-language/move.git", subdir = "language/move-stdlib", rev = "main" }

解释:

MoveStdlib 是指你要依赖的包的包名(即 toml 文件中的 package name )。

git 参数对应的值,是需要依赖的包的 git 仓库地址。

subdir 参数对应的值,是指具体需要依赖的包的目录。

例如,move-language 仓库下面有很多 Move 包,这里我们只使用 move-stdlib,所以就写对应的包目录。

rev 参数对应的值,是指 git 仓库的版本,例如,这里是指它使用 main 主分支,也可以填写一串哈希值(即 commit hash)。

依赖本地的

我们 Move 项目或包下面可能会建多个子目录(其他独立的 Move 包)。那么就会存在本地依赖的问题,比如我需要用到当前项目下的其他 Move 包。

同样,也是在 Move.toml 文件中配置,语法格式如下:

[dependencies]
<package_name> = { local = <string>, addr_subst* = { (<string> = (<string> | "<hex_address>"))+ } } 

我们来测试下。

在当前已有的 Move 包下,新个新的 Move 包。

move new mySubPackage

并在 mySubPackage 下的 sources 目录下,新建 math3.move 文件,内容如下:

module std::math3 {

    public fun multiply(n1:u64, n2:u64): u128 {
        (n1 as u128) * (n2 as u128)
    }

    #[test]
    fun test_multiply() {
        let num:u128 = std::math3::multiply(10u64, 20u64);
        assert!(num==200u128, 1000);
    }
}

OK,我们一个新的 Move 包就创建好了。接下来我们在其他包中,采用本地依赖的方式来依赖这个新创建的包。

即在需要依赖的包的 Move.toml 文件中,增加如下代码:

[dependencies]
MySubPackage= { local = "./MySubPackage" }

local 的值就是包的目录的相对路径

然后,在当前包的 sources 目录下新建个invoke_module.move 文件来导入 MySubPackage 包下的math3模块,代码如下:

script {

    use 0x1::math3;

    fun invoke() {
        let n1:u64 = 99;
        let n2:u64 = 100;
        let n3:u128 = math3::multiply(n1, n2);
        assert!(n3==9900u128, 1000);
    }

}

使用move sandbox 执行下上面的 move 文件进行验证下。OK,验证成功。


所有完整的代码在此:03-move-basic-2