能力(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);  }}