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 反馈 — 无需本地设置。
此课程中的所有课时
- Box、Rc 与 Arc 智能指针
- 内部可变性:RefCell、Cell
- 使用线程实现无畏并发