let x = 5;

let y = &5;

let z= &x;

x 是 i32 整型,y,z 是引用类型,用 & 是取引用符

新加变量 x, 并绑定 5 数据,

新增加变量 y,并绑定 5 数据的引用地址

新增变量 z, 并绑定 x 的引用地址

上面每个变量在内存中都是不同的存储

第一行,在栈中添加变量,而且变量的值是 5,都在栈中

第二行,在栈中增加一个数据 5,再再栈中创建一个变量 y,并把 5 所在的内存地址,给 y;

第三行 在栈中增加一个变量,把 x 所在的内存地址赋值给 z

每行都是在内存新开辟一块空间,存变量,而且变量的值都是不同的,y 和 z 的内存地址并不相同

let x = 5;

let h = &x;

let k = &x;

上面 h,k 拥有相同的值,x 的内存地址确定,h,k 都是取 x 的内存地址,所有 h 和 k 是同等的,根据之前的说明,那如果

let g = &5; 是不是就说明 h==k 是 true, h==g 是 false,k==g 也是 false

其实不然,虽然 g=&5; g 的值和 h,k 都不相同,但是在调用 == 的时候,会产生自动解码的操作,会直接比较指针的值所以,h==g 和 k==g 都是 true

有点需要特别说明,指针的指针这种情况,看下面例子, 先不提有没有必要,编译器是支持的

let x = 5;

let y = &&x;

let z = &&x;

y 和 z 的内存地址是不相同的,具体的原理不是很明白,就根据我的理解(我让我信服的理由)&x 取 x 的引用保存到一块空间,&&x 然后再取这个空间的地址,因为我们并没有把 &x; 存这块空间绑定到一个变量上,所以每次取 &x, 都会产生一个新的空间,所以每次取这个地址都是一个新的内存地址。

最后说下解引用,就是用 * 号来解引用,使用引用指向的数据,如果没有解引用,那就是

let x = 5;

let y = &5;

let z = x+*y;

上面代码没有错,正常编译执行,y 的类型是个 引用类型,* 号把 y 解引用就是 5,相加正常,

如果 z 换成 let z = x+y; 呢,也是正常执行和编译的,这是因为编译器自动把 y 解引用了,

impl<'a,'b> Add<&'a i32> for &'b i32

fn add(self, other: &'a i32) -> >::Output

类似于如何使用 Deref trait 重载不可变引用的 * 运算符,Rust 提供了 DerefMut trait 用于重载可变引用的 * 运算符。

Rust 在发现类型和 trait 实现满足三种情况时会进行解引用强制多态:

当 T: Deref 时从 &T 到 &U。

当 T: DerefMut 时从 &mut T 到 &mut U。

当 T: Deref 时从 &mut T 到 &U。

头两个情况除了可变性之外是相同的:第一种情况表明如果有一个 &T,而 T 实现了返回 U 类型的 Deref,则可以直接得到 &U。第二种情况表明对于可变引用也有着相同的行为。

最后一个情况有些微妙:Rust 也会将可变引用强转为不可变引用。但是反之是 不可能 的:不可变引用永远也不能强转为可变引用。因为根据借用规则,如果有一个可变引用,其必须是这些数据的唯一引用(否则程序将无法编译)。将一个可变引用转换为不可变引用永远也不会打破借用规则。将不可变引用转换为可变引用则需要数据只能有一个不可变引用,而借用规则无法保证这一点。因此,Rust 无法假设将不可变引用转换为可变引用是可能的



作者:zqliang
链接:https://hacpai.com/article/1540173433079
来源:黑客派
协议:CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/

04-30 15:41