【Rust中级教程】1.10. 引用及内部可变性(简单回顾):引用、内部可变性、`Cell`类型及相关操作
1.10 引用及内部可变性简单回顾引用、内部可变性、Cell类型及相关操作这篇文章只对引用与内部可变性进行简单回顾所有权相关内容见 1.9. 所有权简单回顾。1.10.1. 引用通过引用Rust允许将值借用出去但不放弃所有权。引用就是带有附加合约的指针。Rust中一共有两种引用类型。1. 共享的引用共享的引用又叫不可变的引用Rust中写作T其中T指代类型。它的特点在一可以同时或者叫在同一作用域内存在任意数量的引用指向同一个值。每个共享的引用都实现了Copytrait。共享引用背后的值不可变。编译器允许假定共享引用指向的值在该引用存货期间是不会改变的。举个例子一个共享引用的值在某函数内被多次读取那编译器就有权让其只读取一次然后重用读取的值。2. 可变引用与不可变引用相对的就是可变引用在Rust中写作mut T。可变引用是独占的意味着在一个作用域内只能有一个可变引用不能出现第二个可变引用或任意数量的共享引用。所以可变引用没有实现Copytrait共享/不可变引用则实现了Copy。编译器会假定没有其它线程访问可变引用所指向的类型无论是通过共享引用还是可变引用。1.10.2. 拥有值 vs. 拥有到值的可变引用所有者需要对删除值丢弃值负责除此之外两者的作用基本一样。注意如果你移动了可变引用背后的值则必须在其位置上留下另一个值。如果不这样做所有者会认为它需要将其删除丢弃但其实却没有值可以删除了导致未定义行为或编译错误。看个例子fn main() { let mut s String::from(Hello); let r mut s; let t *r; // 试图移动 r 所指向的值 println!({}, r); // r 变成了悬垂引用 }输出error[E0507]: cannot move out of *r which is behind a mutable reference -- src/main.rs:5:13 | 5 | let t *r; // 试图移动 r 所指向的值 | ^^ move occurs because *r has type String, which does not implement the Copy trait | help: consider removing the dereference here | 5 - let t *r; // 试图移动 r 所指向的值 5 let t r; // 试图移动 r 所指向的值 | help: consider cloning the value if the performance cost is acceptable | 5 - let t *r; // 试图移动 r 所指向的值 5 let t r.clone(); // 试图移动 r 所指向的值 |我们来梳理一下过程-r是s的可变引用而*r的操作试图移动这个值String类型没有实现Copytrait意味着s会失去数据- 由于s仍然存在当s作用域结束时Rust期望可以正常释放它的内存- 但s已经被移动走了导致Rust不知道该如何正确释放它从而引发编译错误正确的做法fn main() { let mut s String::from(Hello); let r mut s; let t std::mem::replace(r, String::new()); // 用空字符串替换原值 println!({}, t); // Hello println!({}, s); // }1.10.3. 内部可变性一些类型提供了内部可变性这些类型可以通过共享引用修改值。这些类型通常依赖于额外的机制如原子CPU指令或不变量来提供安全的可变形而不依赖于独占引用的语义。内部可变性分为两类- 通过共享引用获得可变引用:Mutex、RefCell这类类型提供了保障机制——如果对某个值提供了可变引用那么同时或者叫在同一作用域下只会存在一个可变引用并且没有共享引用。这种功能依赖于UnsafeCell类型通过共享引用修改值的唯一正确方式。通过共享引用可以替换值:std::sync::atomic、std::cell::Cell这类类型没有提供可变引用到内部的值但是提供了就地操作值的方法——比如说替换/读取一个值。例如无法获得到usize或i32的直接引用但是可以读取和替换值。1.10.4.Cell类型Cell类型来自于标准库它通过不变量实现内部可变性。-Cell类型无法跨线程共享因为内部值不会被并发地修改即使通过共享引用发生修改- 不会提供到Cell内部的值的引用所以可以一直移动它Cell提供的方法- 对值整体替换也就是所谓的就地操作- 返回值的副本也就是读取1.set(value): 替换值use std::cell::Cell; fn main() { let x Cell::new(10); // 创建一个 Cell存储 10 x.set(20); // 替换内部值 println!(Updated value: {}, x.get()); // 输出 20 }set(value)用新值替换Cell内部的值2.get():返回值的副本use std::cell::Cell; fn main() { let x Cell::new(5); let y x.get(); // 获取 x 内部的副本 println!(Value: {}, y); // 输出 5 }get()不会返回内部值的引用而是返回值的副本适用于实现Copytrait 的类型。适用于i32、bool等实现Copytrait 的类型。