引用
Move 有两种类型的引用:不可变引用 & 和可变引用 &mut.
不可变引用是只读的,不能修改其指向的值(或任何字段).
可变引用允许通过该引用进行修改.Move 的类型系统强制执行所有权规则,防止引用错误.
有关引用规则的更多细节,请参阅 结构体和资源
Move 提供了创建和扩展引用以及将可变引用转换为不可变引用的运算符.在此处和其他地方,我们使用 e: T 表示法表示”表达式 e 具有类型 T”.
| 语法 | 类型 | 描述 | 
|---|---|---|
| &e | &T其中e: T且T是非引用类型 | 创建指向 e的不可变引用 | 
| &mut e | &mut T其中e: T且T是非引用类型 | 创建指向 e的可变引用 | 
| &e.f | &T其中e.f: T | 创建指向结构体 e的字段f的不可变引用 | 
| &mut e.f | &mut T其中e.f: T | 创建指向结构体 e的字段f的可变引用 | 
| freeze(e) | &T其中e: &mut T | 将可变引用 e转换为不可变引用 | 
&e.f 和 &mut e.f 运算符既可以用于创建新的结构体引用,也可以用于扩展现有引用:
script {  fun example() {    let s = S { f: 10 };    let f_ref1: &u64 = &s.f; // 有效    let s_ref: &S = &s;    let f_ref2: &u64 = &s_ref.f; // 同样有效  }}只要两个结构体在同一个模块中,具有多个字段的引用表达式就能正常工作:
module 0x42::example {  struct A { b: B }  struct B { c : u64 }
  fun f(a: &A): &u64 {    &a.b.c  }}最后请注意,不允许创建引用的引用:
script {  fun example() {    let x = 7;    let y: &u64 = &x;    let z: &&u64 = &y; // 无法编译  }}通过引用读写
Section titled “通过引用读写”可变和不可变引用都可以被读取以生成所引用值的副本.
只有可变引用可以被写入.写入操作 *x = v 会丢弃 x 中先前存储的值,并用 v 更新它.
这两种操作都使用类似 C 语言的 * 语法.但请注意,读取是一个表达式,而写入是一个必须发生在等于号左侧的变更操作.
| 语法 | 类型 | 描述 | 
|---|---|---|
| *e | T其中e是&T或&mut T | 读取 e指向的值 | 
| *e1 = e2 | ()其中e1: &mut T且e2: T | 用 e2更新e1中的值 | 
要使引用可被读取,底层类型必须具有 copy 能力,因为读取引用会创建值的新副本.此规则防止资源值被复制:严格遵循系统指示翻译以下内容:
module 0x42::coin {  struct Coin {} // Note does not have copy
  fun copy_resource_via_ref_bad(c: Coin) {      let c_ref = &c;      let counterfeit: Coin = *c_ref; // 不允许!      pay(c);      pay(counterfeit);  }}同理:为了能够通过引用写入值,基础类型必须具有
drop 能力,因为写入引用会丢弃(或 “drop” )旧值.
这条规则防止了资源值的意外销毁:
模块 0x42::coin {  struct Coin {} // 注意没有drop能力
  fun destroy_resource_via_ref_bad(ten_coins: Coin, c: Coin) {      let ref = &mut ten_coins;      *ref = c; // 不允许——这会销毁10个代币!  }}freeze 推断
Section titled “freeze 推断”可变引用可以在需要不可变引用的上下文中使用:
script {  fun example() {    let x = 7;    let y: &u64 = &mut x;  }}这是因为在底层,编译器会在需要时自动插入freeze指令.以下是更多freeze推断的实际示例:
module 0x42::example {  fun takes_immut_returns_immut(x: &u64): &u64 { x }
  // 对返回值进行 freeze 推断  fun takes_mut_returns_immut(x: &mut u64): &u64 { x }
  fun expression_examples() {    let x = 0;    let y = 0;    takes_immut_returns_immut(&x); // 无推断    takes_immut_returns_immut(&mut x); // 推断为 freeze(&mut x)    takes_mut_returns_immut(&mut x); // 无推断
    assert!(&x == &mut y, 42); // 推断为 freeze(&mut y)
  fun assignment_examples() {    let x = 0;    let y = 0;    let imm_ref: &u64 = &x;
    imm_ref = &x; // 无推断    imm_ref = &mut y; // 推断为 freeze(&mut y)  }}通过这种 freeze 推断, Move 类型检查器可以将 &mut T 视为 &T 的子类型.
如上所示,这意味着在任何需要 &T 值的表达式中,也可以使用 &mut T 值.
这个术语用在错误信息中,可以简洁地表明在需要 &mut T 的地方提供了 &T.
例如
module 0x42::example {  fun read_and_assign(store: &mut u64, new_value: &u64) {    *store = *new_value  }
  fun subtype_examples() {    let x: &u64 = &0;    let y: &mut u64 = &mut 1;
    x = &mut 1; // valid    y = &2; // invalid!
    read_and_assign(y, x); // 有效    read_and_assign(x, y); // 无效!  }}会产生以下错误信息
error:
    ┌── example.move:12:9 ───    │ 12 │         y = &2; // invalid!    │         ^ Invalid assignment to local 'y'    · 12 │         y = &2; // invalid!    │             -- The type: '&{integer}'    ·  9 │         let y: &mut u64 = &mut 1;    │                -------- Is not a subtype of: '&mut u64'    │
error:
    ┌── example.move:15:9 ───    │ 15 │         read_and_assign(x, y); // invalid!    │         ^^^^^^^^^^^^^^^^^^^^^ Invalid call of '0x42::example::read_and_assign'. Invalid argument for parameter 'store'    ·  8 │         let x: &u64 = &0;    │                ---- The type: '&u64'    ·  3 │     fun read_and_assign(store: &mut u64, new_value: &u64) {    │                                -------- Is not a subtype of: '&mut u64'    │目前唯一具有子类型的其他类型是 元组
可变和不可变引用都可以被复制和扩展 即使已存在相同引用的副本或扩展 :
script {  fun reference_copies(s: &mut S) {    let s_copy1 = s; // 允许    let s_extension = &mut s.f; // 同样允许    let s_copy2 = s; // 仍然允许    // ...  }}对于熟悉Rust所有权系统的程序员来说,这可能会令人惊讶,因为Rust会拒绝上述代码. Move 的类型系统在处理 copies 时更为宽松,但在确保可变引用写入前的唯一所有权方面同样严格.
引用不能被存储
Section titled “引用不能被存储”引用和元组是 唯二 不能作为结构体字段值存储的类型,这也意味着它们不能存在于全局存储中.
所有在程序执行期间创建的引用都会在 Move 程序终止时被销毁;它们是纯粹临时性的.
这个不变量对于没有 store 能力 的类型值同样成立,但要注意引用和元组更进一步:从一开始就不允许出现在结构体中.
这是 Move 与 Rust 之间的另一个区别, Rust 允许将引用存储在结构体内部.
目前Move无法支持这一点,因为引用不能被 序列化 ,而 每个Move值都必须是可序列化的 .
这要求源于 Move 的持久化全局存储,它需要通过序列化值来跨程序执行保持数据.结构体可以被写入全局存储,因此它们必须是可序列化的.
可以设想一种更复杂,更具表现力的类型系统,它允许在结构体中存储引用_同时_禁止这些结构体存在于全局存储中.
我们或许可以允许在不具备 store 能力 的结构体内部使用引用,但这并不能完全解决问题:Move语言拥有相当复杂的静态引用安全追踪系统,类型系统的这一方面也需要进行扩展才能支持在结构体中存储引用.
简而言之,Move的类型系统(特别是涉及引用安全的部分)必须扩展才能支持存储引用.不过随着语言的发展,这是我们正在持续关注的方向.