Wang's blog

九、标准库 - 智能指针

Published on

Box

Box是最简单的智能指针,它在堆上分配内存。Box只能有一个持有者,类似于C++的std::unique_ptr。Box没有性能开销,同时也没有额外功能。一般在以下情况下使用

  • 要使用一个在编译期无法知道大小的类型
  • 要转换大量数据的所有权但是不想拷贝数据
  • 要使用实现了某个特性,但并不关心其具体类型的类型
// 声明一个Box并储存一个值
let a = Box::new(5);

// 取Box中储存的值(解引用,需要实现Deref特性)
let b = *a;

// 强制解引
println!("a = {}", a);

// 提前释放内存(可自动释放,需要实现Drop特性)
drop(a);

Rc

Rc是带引用计数的智能指针,可以有多个持有者,类似于C++的std::shared_ptr。

// 声明一个Rc并储存一个值
let rc_a: Rc<String> = Rc::new("Rc examples".to_string());

// 复制指针(底层数据不变,引用计数+1)
let rc_b: Rc<String> = Rc::clone(&rc_a);

// 查看引用计数
println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));

// 判断两个Rc是否指向同一数据
println!("rc_a and rc_b are equal: {}", rc_a.eq(&rc_b));

// 通过Rc直接使用底层数据的方法
println!("Length of the value inside rc_a: {}", rc_a.len());

RefCell

使用以上智能指针时,会在编译期检查所有权借用规则(同时只能有一个可变引用或任意个不可变引用),检查不通过则编译失败。RefCell与Box类似,但是在运行时才检查借用规则,如果检查不通过则会panic。

// 使用RefCell指向一个Vec
let v = RefCell::new(vec![1, 2, 3, 4]);

// 可变借用
let mut a = v.borrow_mut();
a.push(5);

// 不可变借用
let b = v.borrow();
println!("{}", b.len());

Weak

Weak指针不增加底层数据的strong_count,只增加其weak_count,因而不影响底层数据的释放。它用于防止形成死锁,类似于C++的std::weak_ptr。

// 声明一个强指针
let strong = Rc::new(100);

// 将其转换为弱指针
let weak = Rc::downgrade(&strong);

// 在使用弱指针前,需要获取对应的强指针,如果该指针已释放,则返回None
let opt: Option<Rc<i32>> = weak.upgrade();

Arc

类似于Rc,用于多线程环境。

let apple = Arc::new("the same apple");

for _ in 0..10 {
    let apple = Arc::clone(&apple);

    thread::spawn(move || {
        println!("{:?}", apple);
    });
}