rust之智能指针总结

rust Jun 15, 2021

前言

最近发现写数据结构需要用智能指针,但是掌握的不好,所以记录下学习的总结。

sized和unsized

对于大部分编译器来说,当把代码编译成二进制格式的时候,需要知道每一个类型的size。但是也有一些类型是没办法确定大小的。unsized类型会被分配到堆上。例如str,String,Vector等。但是编译器在编译struct时候需要确定大小,因为他要分配在栈上。这时候我们需要把他里面的unsize属性,通过指针的方式引用过来。指针类型的大小是确定的。

栈和内存

rust默认是把内存分配在栈上的,除了两个例外:

  1. value的size是unknown。例如 String和Vector这种动态类型。
  2. 当使用Box声明初始化一个变量的时候。

智能指针

指针是保存一段内存地址的变量。
智能指针是除了是一个指针同时它会有元数据信息和其他能力。我们将介绍三种智能指针:

  1. Box<T> 允许数据存储在堆上
  2. Rc<T> 一个可以引用计数的类型,允许多个所有者
  3. Ref<T> 和RefMut<T>, 通过RefCell<T>使用,允许在运行时借用。

Box<T>

功能

Box的作用是让我们可以把数据直接存储在内存上而不是栈上。

使用场景

  1. 数据的size没办法在编译器确定。递归的数据结构类似链表,在编译器的size是没办法确定的,这时候就需要使用Box来存储数据
  2. 数据很大,如果使用copy的话性能很差,这时候可以直接分配在堆上。
  3. 获取一个trait的引用时候,我们不关心具体的类型,只想获取实现了某个trait的数据。

使用Box存储数据在堆上

struct Node{
    value: i32,
}

fn main() {
    let box_node = Box::new(Node { value: 32 });
    println!(" {}", box_node.value);
}


使用Box表示递归类型

我们使用Box来实现类似链表的数据结构


struct Node{
    value: i32,
    next: Box<Option<Node>>
}


fn main() {
    let last = Box::new( Some(Node { value: 11, next: Box::new(None) }));
    let start = Node {value: 11, next: last};
    println!(" {}", start.next.unwrap().value);
}

Rc<T>

在Rust里面value是属于一个变量的。但是在某些情况下,一个value属于多个变量是合理的。例如:图的节点,双链表的节点等。因为没办法在编译器确定所有者,所有者为多个,所以我们需要Rc的帮助。

定义

Rc是Reference counting的缩写。Rc统计有多少个引用存活,当没有引用存活的时候,被引用value就会被释放掉。
ps:Rc只能在单线程中使用,多线程使用的另外的智能指针Arc<T>。

使用Rc实现链表

这个例子是官网,本来想自己写一个来着,失败了...那就用官网的吧

20210615142353


我们来实现两个链表,但是共用后面三个Node。


enum List {
    Node(i32, Rc<List>),
    Nil,
}

fn main() {
    let a = Rc::new(Node(5, Rc::new(Node(10, Rc::new(Nil)))));

    let b = Node(3, Rc::clone(&a));
    let c = Node(4, Rc::clone(&a));

}

Refcell<T>

Refcell的作用我还是不太明白,但是当我们实现经典数据结构的时候,虽然Rc可以让我们把一个value分配给多个变量,但是Rc的所有者都是只读或者说不可变的。Refcell所有的变量可以改变属性把不可变引用变为可变引用。
另外对于Refcell修饰的变量就不会有编译器的引用检查了。
ps: Refcell也是单线程环境下使用

总结

  1. Rc\的value可以拥有多个所有者,Box和Refcell只能有一个所有者
  2. Box对于引用的检查在编译期;Rc在编译期只允许不可变引用。Refcell在运行期检查可变和不可变引用。
  3. 因为Refcell允许在运行期可变引用value,所以即使是Refcell是不可变的,我们也可以改变value。

Ref

  1. https://deepu.tech/memory-management-in-rust/
  2. https://doc.rust-lang.org/book/ch15-00-smart-pointers.html