在 C 语言里,指针的概念就是存储地址的变量。
而 rust 智能指针就是在指针的基础上添加了一些功能。
Box<T>
#
允许将数据存储在堆而不是栈上,然后在栈上保留一个指针指向堆上的数据。
- 特意的将数据分配在堆上
// 此时 1 分配在堆上let n = Box::new(1);
- 当栈上数据较大时,又不想在转移所有权时进行数据拷贝
let arr = [0;1000];// 因为 arr 分配到了栈上,此处发生了深复制let arr2 = arr;
let arr = Box::new([0;1000]);// 没有深复制,发生了所有权转移,arr 不能在访问let arr2 = arr;
- 将动态大小类型变为 Sized 固定大小类型
enum List { Cons(i32, List), Nil,}
以上代码,编译时会报错,因为 Rust 需要在编译时知道类型占用多少空间,而 List 的 Cons 可以无限嵌套。
enum List { Cons(i32, Box<List>), Nil,}
因为 Box 只包含一个指针指向数据和一个指针指向类型,是已知大小的,所以通过。
TODO
- 特征对象
实现不同类型组成的数组
trait Draw { fn draw(&self);}
struct Button { id: u32,}impl Draw for Button { fn draw(&self) { println!("这是屏幕上第{}号按钮", self.id) }}
struct Select { id: u32,}
impl Draw for Select { fn draw(&self) { println!("这个选择框贼难用{}", self.id) }}
fn main() { let elems: Vec<Box<dyn Draw>> = vec![Box::new(Button { id: 1 }), Box::new(Select { id: 2 })];
for e in elems { e.draw() }}
Box::leak
消费掉 Box 并且强制目标值从内存中泄漏(变成全局的值)
TODO 待验证
Box::from_raw
可以收回来
Rc<T>
引用计数(reference counting)#
由于 rust 所有权规则,变量只能有一个所有者,而 Rc 可以使变量有多个所有者。
Rc 通过克隆 RcBox
结构体的指针来实现多个所有者。而 RcBox
包含以下字段:
#[repr(C)]struct RcBox<T: ?Sized> { strong: Cell<usize>, // value 强引用所有者的个数,当为零时清除变量 weak: Cell<usize>, // value 弱引用所有者的个数,不影响变量的清除 value: T,}
let rc = Rc::new("");
// 获取强引用计数Rc::strong_count(&rc);
// 获取弱引用计数Rc::weak_count(&rc);
Weak<T>
#
let rc = Rc::new("a")let weak = Rc::downgrade(&rc)
通过 Rc::downgrade
来生成值的弱应用
Arc (atomic reference counting)#
Rc 的升级版,支持多线程,因为 Rc 需要管理引用计数,但是该计数器并没有使用任何并发原语,因此无法实现原子化的计数操作,最终会导致计数错误。
Cell<T>
#
提供内部可变性的容器(没有额外的性能损耗),不指定 mut 也可以实现修改变量
let c = Cell::new("a");let value = c.get();
// 改变了 c 内部的值c.set("b")
RefCell<T>
#
同 Cell,但是 T 不需要实现 Clone
Cow 写时复制(Copy on Write)#
适用于读多写少的场景。先借用变量进行读操作,当进行写操作时,先对变量进行复制操作后,再进行写操作。
pub enum Cow<'a, B>Where B: 'a + ToOwned + 'a + ?Sized{ Borrowed(&'a + B), // 引用 Owned(<B as ToOwned>::Owned) // 所有者}
// 返回变量的可变引用。当为引用无所有权时,先进行复制后再返回可变引用fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned;
// 转移所有权。当为引用,无所有权时,先进行复制后再返回fn into_owned(self) -> <B as ToOwned>::Owned;
Deref DerefMut#
Deref#
struct MyBox<T>(T);
impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) }}
impl<T> Deref for MyBox<T> { type Target = T; // 关联类型 fn deref(&self) -> &T { &self.0 }}
fn deref_test() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); // *y 会被 Rust 隐式地展开为 *(y.deref())}
当我们将某个特定类型的值引用作为参数传递给函数,但是传入的类型与参数类型不一致时,会自动发生解引用转换
fn hello(name: &str) { println!("Hello, {}", name);}
fn deref_test() { let m = MyBox::new(String::from("Rust")); hello(&m);}
这里将 MyBox<String>
值的引用传入 hello 函数。因为 MyBox 实现了 Deref,所以 Rust 通过 deref 来将 &MyBox<String>
转换为 &String,而标准库的 String 实现了 Deref,所以 Rust 可以继续调用 deref 来将 &String 转换为 &str,并最终与 hello 函数的定义相匹配。
DerefMut#
Drop#
允许我们在变量离开作用域的时候执行某些自定义操作。