0Pricing
Learn Rust Coding · 课时

Box、Rc 与 Arc 智能指针

了解 `Box` 如何分配堆内存、`Rc` 如何实现共享所有权,以及 `Arc` 如何实现线程安全的共享所有权。

Box、Rc 与 Arc 智能指针 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 1 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。

什么是智能指针?

Rust 的所有权系统非常适合确保内存安全,但有时您需要更大的灵活性。这正是智能指针发挥作用的地方!

智能指针是表现得像指针、同时还具有额外元数据和功能的数据结构。它们会自动管理内存、所有权和其他资源。

Box:在堆上存储数据

最简单的智能指针是 Box<T>。它允许您将数据存储在堆上,而不是栈上。

  • 栈:快速、大小固定的数据。
  • 堆:速度较慢、大小灵活的数据,在运行时分配。

当您将一个值放入 Box 时,Box 本身位于栈上,但它所指向的数据位于堆上。

使用 Box 在堆上分配

让我们看看 Box 如何将数据移到堆上。当数据较大,或您在编译时不知道其大小时,这种方式非常有用。

运行这段代码,观察一个值被放入 Box 的过程。

fn main() {
  let x = 5; // x is on the stack
  let boxed_x = Box::new(x); // x's value is moved to the heap, boxed_x is on stack
  println!("Value on stack: {}", x);
  println!("Value in Box (on heap): {}", *boxed_x); // Dereference to get value
}

Box 的适用场景

您可能会在以下情况下使用 Box<T>:

  • 当某种类型的大小在编译时无法确定,而您需要将它存储在大小已知且固定的位置时。
  • 当您拥有大量数据,并希望转移所有权而不复制数据本身时。
  • 当您希望拥有一个特征对象时(例如 Box<dyn Trait>)。

Rc:多个所有者(单线程)

Rust 的所有权规则意味着一个值通常只有一个所有者。但是,如果程序的多个部分都需要“拥有”同一份数据,该怎么办呢?

Rc<T> 即引用计数指针,允许在单线程场景下让多个所有者共享数据。它会记录指向该数据的引用数量。

当计数降为零时,数据就会被清理。

使用 Rc 共享数据

Rc::clone() 会增加引用计数。这不是深拷贝,而只是创建另一个指向同一份数据的指针。

请注意,数据是共享的,并且可以从不同的“所有者”处访问其值。

use std::rc::Rc;

fn main() {
  let value = Rc::new(String::from("Shared String"));
  println!("Count after creation: {}", Rc::strong_count(&value));

  let value_clone_a = Rc::clone(&value); // Increment count
  println!("Count after clone A: {}", Rc::strong_count(&value));

  {
    let value_clone_b = Rc::clone(&value); // Increment count
    println!("Count after clone B: {}", Rc::strong_count(&value));
    println!("Data from A: {}", value_clone_a);
    println!("Data from B: {}", value_clone_b);
  } // value_clone_b goes out of scope, count decreases

  println!("Count after B goes out of scope: {}", Rc::strong_count(&value));
}

Arc:共享所有权(多线程)

Rc<T> 非常适合单线程应用。不过,如果您需要在线程之间共享数据,Rc<T> 就不安全了。

Arc<T> 即原子引用计数指针,是 Rc<T> 的线程安全版本。它使用原子操作更新引用计数,从而确保跨线程的安全性。

由于使用了原子操作,Arc 相比 Rc 会产生少量性能开销。

使用 Arc 跨线程共享数据

此示例展示了 Arc 如何让多个线程安全地访问和读取同一份共享数据。每个线程都会获得自己的 `Arc` 副本。

主线程会等待所有生成的线程完成。

use std::sync::Arc;
use std::thread;

fn main() {
  let data = Arc::new(vec![1, 2, 3, 4, 5]);
  let mut handles = vec![];

  for i in 0..3 {
    let data_clone = Arc::clone(&data); // Clone Arc for each thread
    let handle = thread::spawn(move || {
      println!("Thread {} has data: {:?}", i, *data_clone);
    });
    handles.push(handle);
  }

  for handle in handles {
    handle.join().unwrap();
  }
  println!("All threads finished.");
}

Box、Rc 还是 Arc?

选择合适的智能指针取决于您的需求:

  • Box<T>:当您需要将数据放在堆上时使用,通常适用于单一所有权或递归类型。
  • Rc<T>:当您需要在单线程环境中为数据设置多个所有者时使用。
  • Arc<T>:当您需要在多线程(并发)环境中为数据设置多个所有者时使用。

如果不需要线程安全,请优先使用 Box 或 Rc,因为 Arc 会带来性能开销。

智能指针检查

您需要将一个大型图像文件存储在堆上,并且程序中只有一个部分会拥有和管理它。您应该使用哪种智能指针?

回顾:智能指针

在本课中,您学习了 Rust 中三种基本的智能指针:

  • Box<T>:用于在单一所有权下将数据分配到堆上。
  • Rc<T>:用于在单线程环境中让数据拥有多个所有者。
  • Arc<T>:用于在多线程环境中让数据拥有多个线程安全的所有者。

这些智能指针让您能够更好地控制内存管理,同时继续享受 Rust 的安全保证。

常见问题解答

「Box、Rc 与 Arc 智能指针」课时是免费的吗?

是的 — 「Box、Rc 与 Arc 智能指针」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。

「Box、Rc 与 Arc 智能指针」这节课中我会学到什么?

了解 `Box` 如何分配堆内存、`Rc` 如何实现共享所有权,以及 `Arc` 如何实现线程安全的共享所有权。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Learn Rust Coding 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 3 节。

「Box、Rc 与 Arc 智能指针」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Learn Rust Coding 课中编写并运行代码吗?

能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Box、Rc 与 Arc 智能指针
  2. 内部可变性:RefCell、Cell
  3. 使用线程实现无畏并发
← 返回 Learn Rust Coding