能力(Abilities)
能力是Move语言中的类型特性,用于控制特定类型值允许执行的操作.该系统对值的”线性”类型行为提供了细粒度控制,同时决定了值在全局存储中的使用方式和条件.这是通过在特定字节码指令上设置访问限制来实现的——要使用某个字节码指令处理值,该值必须具备相应的能力(并非所有指令都受能力限制).
四种能力类型
Section titled “四种能力类型”Move定义了四种能力:
copy
能力允许具备该能力的类型的值被复制.它控制着以下操作权限:
若某值具有 copy
能力,则该值内部包含的所有值都具备 copy
能力.
drop
能力允许具备该能力的类型的值被丢弃.这里的”丢弃”指值未被转移,而是在Move程序执行过程中被有效销毁.该能力控制着在以下场景中忽略值的权限:
- 未使用局部变量或参数中的值
- 未在 通过
;
的序列 中使用值 - 在 赋值 时覆盖变量中的值
- 通过 写入
*e1 = e2
覆盖引用值
若某值具有 drop
能力,则该值内部包含的所有值都具备 drop
能力.
store
能力允许具备该能力的类型的值存在于全局存储的结构体(资源)中,但 不一定 能作为顶级资源存在于全局存储.
这是唯一不直接控制操作的能力,而是与 key
能力配合使用时,控制值在全局存储中的存在权限.
若某值具有 store
能力,则该值内部包含的所有值都具备 store
能力.
key
能力允许类型作为 全局存储操作 的键使用.
它控制着所有全局存储操作,因此若要将某类型用于 move_to
,borrow_global
,move_from
等操作,
该类型必须具有 key
能力.注意这些操作仍必须在定义 key
类型的模块中使用(从某种意义上说,这些操作对定义模块是私有的).
如果一个值具有 key
,那么该值内部包含的所有值都必须具有 store
.这是唯一具有这种不对称特性的能力.
大多数原始内置类型都具有 copy
,drop
和 store
能力,除了 signer
只具有 drop
:
bool
,u8
,u16
,u32
,u64
,u128
,u256
和address
都具有copy
,drop
和store
signer
具有drop
- 不可复制,也不能放入全局存储
vector<T>
可能具有copy
,drop
和store
,具体取决于T
的能力- 详见 条件能力与泛型类型
- 不可变引用
&
和可变引用&mut
都具有copy
和drop
- 这里指的是复制和删除引用本身,而非它们引用的内容
- 引用不能出现在全局存储中,因此它们没有
store
所有原始类型都不具有 key
,意味着它们都不能直接用于 全局存储操作.
要为 struct
声明能力,需在结构体名称之后,字段之前使用 has <能力>
语法.例如:
module 0x42::example { struct Ignorable has drop { f: u64 }
struct Pair has copy, drop, store { x: u64, y: u64 }}
此例中:Ignorable
具有 drop
能力,Pair
具有 copy
,drop
和 store
.
所有这些能力都对受控操作有严格要求.只有当值具有相应能力时才能执行操作——即使该值深度嵌套在其他集合中!
因此:在声明结构体能力时,会对字段提出特定要求.所有字段必须满足这些约束条件.这些规则确保结构体符合上述能力的可达性规则.如果结构体声明具有…
copy
,则所有字段必须具有copy
drop
,则所有字段必须具有drop
store
,则所有字段必须具有store
key
,则所有字段必须具有store
key
是目前唯一不要求自身能力的能力
例如:
module 0x42::example { // 没有任何能力的结构体 struct NoAbilities {}
struct WantsCopy has copy { f: NoAbilities, // 错误:'NoAbilities' 没有 'copy' 能力 }}
类似地:
module 0x42::example { // 没有任何能力的结构体 struct NoAbilities {}
struct MyResource has key { f: NoAbilities, // 错误:'NoAbilities' 没有 'store' 能力 }}
条件能力与泛型类型
Section titled “条件能力与泛型类型”当泛型类型上标注能力时,并非该类型的所有实例都保证拥有这些能力.考虑以下结构体声明:
module 0x42::example { struct Cup<T> has copy, drop, store, key { item: T }}
如果 Cup
能持有任意类型(无论其能力如何)会非常有用.类型系统可以_看到_类型参数,因此当它_发现_某个类型参数会违反该能力的保证时,就应该能够从 Cup
中移除相应能力.
这种行为初听起来可能有些困惑,但如果我们考虑集合类型可能会更容易理解.我们可以认为内置类型 vector
具有以下类型声明:
vector<T> has copy, drop, store;
我们希望 vector
能适用于任何类型,不希望为不同能力创建单独的 vector
类型.那么我们希望的规则是什么?正是与上述字段规则完全相同的逻辑.因此,只有当内部元素可复制时,复制 vector
值才是安全的;只有当内部元素可被忽略/丢弃时,忽略 vector
值才是安全的;只有当内部元素能存在于全局存储时,将 vector
放入全局存储才是安全的.
为了实现这种额外的表达能力,类型的实际能力可能与其声明的能力不同,这取决于该类型的实例化方式.实际上,类型的能力取决于其声明和类型参数的组合.对于任何类型,类型参数都会被悲观地假设为在结构体内部使用,因此只有当类型参数满足上述字段要求时,才会授予相应能力.以前文的 Cup
为例:
- 只有当
T
具有copy
能力时,Cup
才具有copy
能力 - 只有当
T
具有drop
能力时,Cup
才具有drop
能力 - 只有当
T
具有store
能力时,Cup
才具有store
能力 - 只有当
T
具有store
能力时,Cup
才具有key
能力
以下是针对每种能力的条件系统示例:
示例:条件性 copy
Section titled “示例:条件性 copy”module 0x42::example { 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>) { // 无效,'Cup<signer>' 不具有 'copy' // 尽管 'Cup' 声明了 copy,但该实例不具备 'copy' // 因为 'signer' 不具有 'copy' let c_account2 = copy c_account; // 无效,'Cup<NoAbilities>' 不具有 'copy' // 因为 'NoAbilities' 不具有 'copy' let c_n2 = copy c_n; }}
示例:条件性 drop
Section titled “示例:条件性 drop”module 0x42::example { struct NoAbilities {}
struct S has copy, drop { f: bool }
struct Cup<T> has copy, drop, store { item: T }
fun unused() { Cup<bool> { item: true }; // 有效,'Cup<bool>' 具有 'drop' Cup<S> { item: S { f: false } }; // 有效,'Cup<S>' 具有 'drop' }
fun left_in_local(c_account: Cup<signer>): u64 { let c_b = Cup<bool> { item: true }; let c_s = Cup<S> { item: S { f: false } }; // 有效返回:'c_account'、'c_b' 和 'c_s' 有值 // 但 'Cup<signer>'、'Cup<bool>' 和 'Cup<S>' 具有 'drop' 0 }
fun invalid_unused() { // 无效,不能忽略 'Cup<NoAbilities>' 因为它不具有 'drop' // 尽管 'Cup' 声明了 'drop',但该实例不具备 'drop' // 因为 'NoAbilities' 不具有 'drop' Cup<NoAbilities> { item: NoAbilities {} }; }
fun invalid_left_in_local(): u64 { let c_n = Cup<NoAbilities> { item: NoAbilities {} }; // 无效返回:'c_n' 有值 // 且 'Cup<NoAbilities>' 不具有 'drop' 0 }}
示例:条件性 store
Section titled “示例:条件性 store”module 0x42::example { 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' 能力 }}
示例:条件性 key
Section titled “示例:条件性 key”module 0x42::example { struct NoAbilities {}
struct MyResource<T> has key { f: T }
fun valid(account: &signer) acquires MyResource { let addr = signer::address_of(account); // 有效,'MyResource<u64>' 具有 'key' 能力 let has_resource = exists<MyResource<u64>>(addr); if (!has_resource) { // 有效,'MyResource<u64>' 具有 'key' 能力 move_to(account, MyResource<u64> { f: 0 }) }; // 有效,'MyResource<u64>' 具有 'key' 能力 let r = borrow_global_mut<MyResource<u64>>(addr) r.f = r.f + 1; }
fun invalid(account: &signer) { // 无效,'MyResource<NoAbilities>' 不具备 'key' 能力 let has_it = exists<MyResource<NoAbilities>>(addr); // 无效,'MyResource<NoAbilities>' 不具备 'key' 能力 let NoAbilities {} = move_from<NoAbilities>(addr); // 无效,'MyResource<NoAbilities>' 不具备 'key' 能力 move_to(account, NoAbilities {}); // 无效,'MyResource<NoAbilities>' 不具备 'key' 能力 borrow_global<NoAbilities>(addr); }}