前言
之前我们曾提到不同于其他的智能合约编程语言,Move将脚本和模块分离,之前的两篇文章主要与脚本相关,这篇文章将会主要介绍模块相关的内容。
模块
模块是开发者发布在自己地址的一系列函数,之前我们使用的脚本只能使用已经发布的模块或者标准库,标准库也就是发布在0x1地址的一系列模块。
❝模块是发布在发送者的地址的,标准库是发布在0x1地址的,当发布一个模块时它的函数并未执行,需要使用use script去执行模块。
❞
模块是以module关键字开头,紧接着的是模块明,之后双括号里面就是模块的内容:
module Math {
//模块内容
public fun sum(a: u64, b: u64): u64 {
a + b
}
}
❝模块是唯一的向其他人开放代码的方式,新的类型和资源只能定义在模块里。
❞
默认的你的模块会在你的地址编译和发布,然而如果需要使用本地的模块(用作测试和开发)可以在模块文件中指定地址即可。
address 0x1 {
module Math {
//模块内容
public fun sum(a: u64, b: u64): u64 {
a + b
}
}
}
引入模块
Move中默认的上下文是空的,这意味着你只能使用基本类型如interger,bool和address,而且智能操作这些基本类型和变量,为了实现更加复杂的功能,你可以引入已经发布的模块或者标准库。
直接引入
可以直接通过地址引入模块
script {
fun main(a: u8) {
0x1::Offer::assert!(a == 10, 1);
}
}
在这个例子中我们使用了地址01x(标准库)的模块Offer中的方法assert!(expr: bool, code: u8)。
使用关键字use
为了使代码更加简洁,可以使用use关键字引入模块。
use 0x1::Vector;
引入模块后需要使用模块的内容需要使用::
script {
use 0x1::Vector;
fun main(a: u8) {
let _ = Vector::empty<u64>();
}
}
与此同时还可以引入指定的内容
script {
//引入一个成员
use 0x1::Signer::address_of;
//引入多个成员
use 01x::Vector::{
empty,
push_back
}
}
为了避免命名冲突,可以在引入是使用as取别名
script {
//引入一个成员
use 0x1::Signer::address_of as addr;
//引入多个成员
use 01x::Vector::{
empty as em,
push_back as pu
}
}
常量
Move支持定义脚本级别和模块级别的常量,一旦定义就无法更改。
script {
use 0x1::Debug;
const RECEIVER : address = 0x999;
fun main(account: &signer) {
Debug::print<address>(&RECEIVER);
// they can also be assigned to a variable
let _ = RECEIVER;
// but this code leads to compile error
// RECEIVER = 0x800;
}
}
函数
在Move中函数是唯一执行的地方,函数需要使用fun关键字声明,之后是函数名和参数列表,但括号内的是函数体。
fun function_name(arg1: u64, arg2: bool) {
//函数体
}
❝在Move中函数名需要都小写然后通过下划线连接。
❞
脚本中的函数
脚本中只包含一个main函数,这个函数带有参数是用来之行一次事务,限制比较多,没有返回值,可以用来执行已经发布的模块的函数,下面的脚本就是用来检查地址是否存在。
script {
use 0x1::Account;
fun main(addr: address) {
assert!(Account::exists(addr));
}
}
模块中的函数
模块事一系列解决一个或多个任务的函数和类型(之后会详细介绍)的集合。在这一部分我们会创建一个简单的Math模块,提供一系列基本的算术能力。
module Math {
fun zero(): u8 {
0
}
}
❝需要注意的是函数可以用return来终止函数执行并且返回一个值,以上就是返回来一个零,这是我们在表达式中提到到,没有分号可以省略return关键字。
❞
返回多个返回值
在Move中函数是可以返回多个返回值的:
module Math {
public fun max(a: u8, b: u8): (u8, bool) {
if (a > b) {
(a, false)
} else if {
(b, false)
} else {
(a, true)
}
}
}
我们已经返回了两个值,那么又该如何接受那两个值呢:
script {
use 0x1::Debug;
use 0x1::Math;
fun main(a: u8, b: u8) {
let (max, is_equal) = Math::max(99, 100);
assert(is_equal, 1);
Debug::print<u8>(&max)
}
}
函数的可见性
Move的函数主要可以分为两类,一类事private(默认的),一类事public,如果时public则是其他模块也可以访问,但是如果时private,则只能在定义函数的地方访问。例子如下,is_zero和zero函数都是在Math模块内,但是zero是private,is_zero可以访问zero,但是其他模块不可以访问zero,而is_zero是public其他模块也可以访问。
module Math {
public fun is_zero(a: u8): bool {
a == zero()
}
fun zero(): u8 {
0
}
}
native函数
Move有一种特殊类型的函数native函数,native函数是实现Move没有提供的能力,实现的方式也有很多种,其室友虚拟机自己定义的,例子如下
module Signer {
native public fun borrow_address(s: &signer): &address;
// ... some other functions ...
}
最后
这篇文章主要讲述了Move的模块和函数,更多文章可以关注公众号QStack。