0x1 Move数据类型
一般常见的基础数据类型在Move语言中都有
1.整型
整型有u8
值范围2的8次方- 1, u16
,u32
值范围是2的32次方-1,u64
,u128
,u256
值范围是2的256次方-1。整型数值是隐式值拷贝的,因此可以无需copy
修饰。
2.布尔
布尔有:bool
,太常见了,只有true
或者false
3.地址类型
地址类型: address
,占128-bit(16字节)大小,隐式可拷贝,无需显式声明copy
。一般表达为
let a1: address = @0x1; // 全称是 0x00000000000000000000000000000001
let a2: address = @0x42; // 全称是 0x00000000000000000000000000000042
let a3: address = @0xDEADBEEF; // 全称是 0x000000000000000000000000DEADBEEF
let a4: address = @0x0000000000000000000000000000000A;
let a5: address = @std; // 把命名地址std分配给变量a5
let a6: address = @66; // 数字表示
let a7: address = @0x42; // 十六进制表示
4.特殊类型signer
一个特殊类型signer
,它是resource type
也就是资源类型
,它是一种特殊的address
类型;signer也可以说类似于Unix系统中的UID,表示用户在Move语言代码外的一种鉴权,比如检查加密签名或者密码之类的。signer不能被copy修饰,自身也不是copyable。
5.数组类型vector
没什么特别说的,直接看例子吧
use std::vector;
let v = vector::empty<u64>();
vector::push_back(&mut v, 5);
vector::push_back(&mut v, 6);
assert!(*vector::borrow(&v, 0) == 5, 42);
assert!(*vector::borrow(&v, 1) == 6, 42);
assert!(vector::pop_back(&mut v) == 6, 42);
assert!(vector::pop_back(&mut v) == 5, 42);
6.结构体类型
结构体struct,跟Solidity类似的是,Move也有自定义结构体,
例如:
module m {
struct Foo { x: u64 }
public fun test1() {
let foo = Foo { x: 100 };
let Foo { x: _ } = foo;
// 到这里,函数返回时,foo会被释放
}
}
结构体可以被四种能力(Abilities
)修饰,分别是:copy、drop、store、key。
copy
表示值可以被拷贝drop
表示值可以被弹出或者删除store
表示值存储在全局存储(global storage)的结构体中key
允许类型作为全局存储操作的键
例如,说说copy
的规则:
struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
fun example(c_x: Cup<u64>, c_s: Cup<S>) {
// 允许执行!因为Cup<u64>拥有copy,并且u64隐式拥有copy能力
let c_x2 = copy c_x;
// 允许执行!因为Cup<S>有copy,S也有copy能力
let c_s2 = copy c_s;
}
fun invalid(c_account: Cup<signer>, c_n: Cup<NoAbilities>) {
// 不允许执行!因为signer没有copy,即使Cup拥有copy但是signer没有,所以会报错
let c_account2 = copy c_account;
// 不允许执行!因为NoAbilities没有copy能力,即使Cup有copy也不可以
let c_n2 = copy c_n;
}
例如,说说store
的规则:
struct Cup<T> has copy, drop, store { item: T }
// MyInnerResource被声明具有store,所以结构体里的字段都必须是store
struct MyInnerResource has store {
yes: Cup<u64>, // 合法,因为Cup<u64>具有store修饰
// no: Cup<signer>, 不合法,因为Cup<signer>不具有store修饰
}
// MyResource被声明为具有key,所以结构体里的所有字段必须具有store修饰
struct MyResource has key {
yes: Cup<u64>, // 合法,因为Cup<u64>具有store修饰
inner: Cup<MyInnerResource>, // 合法,因为Cup<MyInnerResource>具有store修饰
// no: Cup<signer>, 不合法,因为Cup<signer>不具有store修饰
}
引用&和可变引用&mut
// 1.有一个结构体
let foo = Foo { x: 3, y: true };
// 2.引用访问
let foo_ref: &Foo = &foo; // 拷贝一个引用,现在有两个引用
let y: bool = foo_ref.y; // 引用访问字段y
let x_ref: &u64 = &foo.x; // 引用访问字段x
// 3.可变引用拷贝
let x_ref_mut: &mut u64 = &mut foo.x;
*x_ref_mut = 42; // 通过可变引用修改原引用的x的值
0x2 Move函数
函数分两种,一种是module function,另一种是script function。
fun <identifier><[type_parameters: constraint],*>([identifier: type],*): <return_type> <acquires [identifier],*> <function_body>
修饰符:
- 默认是
internal
,也就是fun关键字前面什么也不写,就是internal的,那么外部的module和script都不可以调用 - 使用
public
修饰可见性,外部module和script都可以访问 - 使用
entry
修饰,一般表示入口调用函数,只是字面意义上,不能杜绝调用,因此将它修饰internal,而非public - 使用
public(friend)
修饰可见性,表示指定的module可以访问,否则拒绝。
例如public(friend)
修饰可见性:
address 0x42 {
module m {
friend 0x42::n; // friend declaration
public(friend) fun foo(): u64 { 0 }
fun calls_foo(): u64 { foo() } // valid
}
module n {
fun calls_m_foo(): u64 {
0x42::m::foo() // 合法调用!
}
}
module other {
fun calls_m_foo(): u64 {
0x42::m::foo() // 非法调用!因为 module other不是 module m 的friend
}
}
}
script {
fun calls_m_foo(): u64 {
0x42::m::foo() // 非法调用!因为此 script不是 module m 的friend
}
}
acquires关键字修饰
当一个函数需要操作resource时,则需要使用acquires
关键字修饰,操作或者访问resource时,有三个针对全局存储的操作,分别是:move_from
, borrow_global
, borrow_global_mut
例如,减去指定地址的余额:
address 0x42 {
module example {
struct Balance has key { value: u64 }
public fun add_balance(s: &signer, value: u64) {
move_to(s, Balance { value })
}
public fun extract_balance(addr: address): u64 acquires Balance {
let Balance { value } = move_from(addr); // acquires needed
value
}
}
}