智能指针
-
表现的像一个指针,拥有数据并允许在对数据进行维护。
-
通常通过
struct
实现并实现两个特性Deref
和Drop
Deref
允许智能指针实例行为像一个引用,让代码可以同时处理引用和智能指针Drop
允许自定义智能指针超出作用域的行为。
-
标准库常见的智能指针
Box<T>
用于在堆分配值Rc<T>
引用计数类型,允许多个拥有者Ref<T>
和RefMut<T>
和通过RefCell<T>
访问,运行时取代编译期强制检查借用规则
Box<T>
场景:
-
编译期未知大小的类型(递归类型(自己包含自己类型的类型,如链表)编译期无法确定大小)
// 递归类型 enum List { Cons(i32, Box<List>), Nil, } fn main() { let b = Box::new(5); println!("b = {}", b); let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); }
-
避免大量数据转移所有权时发生拷贝
-
拥有一个实现特定特性的值(不关心具体类型)的所有权
Deref
用于自定义解引用操作符( *
) 的行为,智能指针通过实现该特性来模拟普通引用的行为。
对比
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y); // must dereference
}
和
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
Box<T>
的实现大体如下
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
fn deref(&self) -> &T {
&self.0
}
}
deref
让编译器知道如何通过 &
获取一个引用,然后就可以正确的解引用,实际展开的代码如下:
*(y.deref())
这样做的原因是如果 deref
不返回引用而是返回值,那么就会发生所有权转移,这是智能指针所不允许的。
如果一个类型实现了 Deref
,当传递给函数或方法的类型不满足时,编译器会自动进行隐式转换(可能时多次)以满足需求。
还有 DerefMut
处理可变解引用。
- 当
T: Deref<Target=U>
时,从&T
变为&U
- 当
T: DerefMut<Target=U>
时,从&mut T
变为&mut U
- 当
T: Deref<Target=U>
时,从&mut T
变为&U