跳转到主要内容
模块 6 · 第 2 课(共 2)课程中的12/14~15 min
模块课程(2/2)

智能指针:Box 和 Rc

智能指针是一种行为类似于指针的数据结构,但具有元数据和额外功能(例如引用计数或自动内存管理)。

在 Rust 中,普通引用(如 &)仅借用数据。另一方面,智能指针通常拥有它们指向的数据。常见的示例包括 String 和 Vec<T>,但标准库提供了其他专用的示例。


用于堆分配的 Box<T>

最简单的智能指针是**Box<T>,它将数据存储在堆**而不是堆栈上。只有指向堆分配数据的指针保留在堆栈上。

主要用于以下几种情况:

  1. 当您的类型在编译时无法知道其大小时(例如递归类型),并且您希望在需要精确大小的上下文中使用该类型的值。
  2. 当您想要转移大量数据的所有权而不复制它时。

基本示例:

Rust
fn main() {
    let b = Box::new(5); // allocates the value 5 on the heap
    println!("b = {}", *b); // dereferences with * to read the value
}

递归类型和缺点列表

递归类型是一种可以包含自身值作为其定义的一部分的类型。 Rust 必须在编译时知道类型的确切大小。由于递归类型的深度可能是无限的,除非您使用 Box(它具有固定大小,因为它是指针),否则编译器会生成错误:

Rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}

Rc<T>:引用计数

在某些情况下,单个值可以有多个所有者。 Rc<T>(引用计数)智能指针跟踪对某个值的引用次数,以确定该值是否仍在使用中。如果计数降至零,则安全地释放资源。

[!注意] Rc<T> 仅适用于单线程场景。对于多线程(并发)场景,必须使用 Arc<T> (原子引用计数)。

Rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a); // increments the reference count to 2
    println!("References: {}", Rc::strong_count(&a));
}

自己尝试一下

练习 1:使用 Box 进行堆分配

锻炼#rust.m6.l2.e1
尝试:0加载中...

使用 Box::new 在堆上分配整数值 42 并将其分配给名为 val 的变量。接下来,通过使用 * 运算符显式取消引用 Box 中存储的值来打印它。

正在加载编辑器...
显示提示

使用 `let val = Box::new(42);` 将值存储在堆中,并在 `println!` 中使用 `*val` 取消引用它。

3 次尝试后可用的解决方案

练习 2:递归数据结构(缺点列表)

锻炼#rust.m6.l2.e2
尝试:0加载中...

定义一个名为 List 的递归枚举,包含两个变体:Cons 包装元组 (i32, Box<List>),Nil 表示列表末尾。在 main 函数中,实例化一个包含元素 1 后跟 Nil 的链表。

正在加载编辑器...
显示提示

枚举被声明为“enum List { Cons(i32, Box<List>), Nil }”。使用“List::Cons(1, Box::new(List::Nil))”实例化列表。

3 次尝试后可用的解决方案

练习 3:与 Rc 共享所有权

锻炼#rust.m6.l2.e3
尝试:0加载中...

使用 std::rc::Rc 创建一个共享整数,变量 a 中包含值 100。使用 Rc::clone 将引用克隆到变量 b 中。最后,使用 Rc::strong_count 函数打印活动引用的计数。

正在加载编辑器...
显示提示

使用“Rc::new(100)”创建值。使用“Rc::clone(&a)”克隆并使用“Rc::strong_count(&a)”显示所有者计数。

3 次尝试后可用的解决方案