智能指针

  • 表现的像一个指针,拥有数据并允许在对数据进行维护。

  • 通常通过 struct 实现并实现两个特性 DerefDrop

    • 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