引用(referencing)和溯源(dereferencing)是铁锈语言学习和应用中相当常见的概念, 有必要深入研究。有了深层次的理解才能做到使用时胸有成竹、得心应手。

为了不转移数据的“所有权”,铁锈创造了“借用”的概念:

let a = 10;
let b = &a;

在变量前面加上 & 符号,便创建了一个对于该变量的引用。 此处变量 b 就是 a 的引用,它可以 “借用” a 的值,但是又不夺走 a 对值的所有权。

引用符号 & 没必要紧贴着变量名。& a 也是合法的引用操作,只是相当不美观。

引用是一种数据结构

上述代码中,b 的类型是一个称为“引用”的特殊数据结构。 该数据结构在内存中的造型类似于指针,是默认分配在 上的一小段空间:

引用类型内部除了目标数据的起始地址之外,应该还记录了目标数据的类型、引用方式等信息。 由于内存访问安全因素,铁锈不会直接让程序员查看和操作这些危险的信息,因此这个数据结构对于程序员来说比较抽象。 但请记住,它和 StringVec<T> 一样,是铁锈内建的数据结构。

这里变量 b 的类型是 &i32,可以读作“32位整型数值的(只读)引用”。 尽管不是基本数值类型,但它们之间的赋值操作是数据“复制”:

let a = 10;
let b = &a;
let c = b;
println!("c={}", c); // => 10
println!("b={}", b); // => 10

看起来如此!只读引用 b 在将值赋给 c 之后仍然可用,说明没有转移所有权,而是把自己的数据原样复制一份给 c。

那么如果 b 是一个可写引用呢?

let mut a = 10;
let b = &mut a;
let c = b;
println!("c={}", c);
println!("b={}", b);

这里 b 的类型是 &mut i32,可以读作“32位整型数值的可写引用”。

这里的赋值行为终于导致了“所有权已经转移”的错误:

error[E0382]: borrow of moved value: `b`
  --> src/main.rs:10:22
   |
7  |     let b = &mut a;
   |         - move occurs because `b` has type `&mut i32`, which does not implement the `Copy` trait
8  |     let c = b;
   |             - value moved here
9  |     println!("c={}", c);
10 |     println!("b={}", b);
   |                      ^ value borrowed here after move

仔细看错误信息,我们可以得知,可写引用类型之所以发生了所有权转移,是因为它 没有实现 Copy 特性。 换言之,只读引用类型实现了 Copy 特性,因而在赋值时,将自身的值复制了一份,而没有转移数值的所有权。

(未完待续)