内部可变性:RefCell、Cell
学习使用 `RefCell` 和 `Cell` 实现内部可变性,从而安全地通过不可变引用访问可变数据。
内部可变性:RefCell、Cell 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。
什么是内部可变性?
在 Rust 中,如果您已经拥有数据的不可变引用,借用规则通常会阻止您再拥有该数据的可变引用。这可以确保数据安全。
内部可变性是一种设计模式,即使您只有数据的不可变引用,也允许您修改数据。它可以看作是对 Rust 常规规则的受控例外,用于特定场景。
`Cell<T>`:用于可复制类型
Cell<T> 类型为实现了 Copy 特征的类型(例如整数、布尔值和字符)提供内部可变性。它通过替换其中的值来实现这一点。
get():返回Cell内部值的副本。set(value):用新值替换Cell内部的值。
对于小型、可复制的数据,它简单而高效。
使用 `Cell` 更新值
下面是一个 Cell 的基本示例。请注意,即使 Cell 本身声明为不可变,我们仍然可以更改其中的值。
use std::cell::Cell;
fn main() {
let my_num = Cell::new(10);
println!("Initial value: {}", my_num.get());
my_num.set(20);
println!("Updated value: {}", my_num.get());
let x = &my_num;
x.set(30); // Still works via immutable reference!
println!("Via immutable ref: {}", my_num.get());
}`RefCell<T>`:用于不可复制类型
当您需要为未实现Copy 的类型(例如 String、Vec 或自定义结构体)实现内部可变性时,应使用 RefCell<T>。
RefCell 允许您获取内部数据的可变引用,但它会在运行时而不是编译时强制执行 Rust 的借用规则。
`RefCell` 的运行时借用
RefCell 提供了返回智能指针的方法:
borrow():返回一个Ref(不可变智能指针)。您可以同时拥有多个不可变借用。borrow_mut():返回一个RefMut(可变智能指针)。您一次只能拥有一个可变借用。
如果您在运行时违反这些规则,程序将执行 panic!。
使用 `RefCell`(不可变借用)
这里,我们使用 borrow() 获取 RefCell 内部 String 的不可变引用,然后就可以打印其内容。
use std::cell::RefCell;
fn main() {
let my_string_cell = RefCell::new(String::from("Hello"));
let s1 = my_string_cell.borrow();
println!("Value: {}", *s1); // Dereference Ref to get String
let s2 = my_string_cell.borrow(); // Multiple immutable borrows are fine
println!("Another value: {}", *s2);
}使用 `RefCell`(可变借用)
现在,让我们使用 borrow_mut() 获取可变引用。这样就可以修改 String。请注意,一旦 s_mut 进入作用域,就不允许存在其他借用(可变或不可变)。
use std::cell::RefCell;
fn main() {
let my_string_cell = RefCell::new(String::from("World"));
{ // Scope for the mutable borrow
let mut s_mut = my_string_cell.borrow_mut();
s_mut.push_str(", Rust!");
}
// s_mut is out of scope here, so we can borrow again
let s_final = my_string_cell.borrow();
println!("Final value: {}", *s_final);
}`RefCell` 的运行时崩溃
如果您在另一个可变借用(或任何不可变借用)处于活动状态时尝试获取可变借用,RefCell 会使程序在运行时执行 panic!。这可以防止数据损坏。
运行这段代码,看看实际效果!程序会因“借用已在使用中”错误而崩溃。
use std::cell::RefCell;
fn main() {
let my_value = RefCell::new(vec![1, 2, 3]);
let _first_mut_borrow = my_value.borrow_mut();
println!("First mutable borrow is active.");
// This line will cause a runtime panic!
// Try commenting it out to see the program run successfully.
let _second_mut_borrow = my_value.borrow_mut();
println!("This line will not be reached.");
}`Cell` 与 `RefCell` 总结
选择 Cell 还是 RefCell,取决于数据类型以及您与数据交互的方式:
Cell<T>:用于实现了Copy的类型。它会替换整个值,通常更简单且性能更好。RefCell<T>:用于未实现Copy的类型。它提供对内部数据的引用,并在运行时强制执行借用规则,因此支持更复杂的修改操作。
快速检查:Cell 还是 RefCell?
您有一个 struct,需要存储一个可变计数器(u32)和一个可变名称列表(Vec<String>),并且需要通过对该 struct 的不可变引用来更新这两个字段。对于每个字段,您会使用哪种内部可变性类型?
回顾:内部可变性
您已经学习了 Rust 的内部可变性模式。它允许您以受控且安全的方式,通过不可变引用修改数据。
Cell<T>:用于Copy类型,替换内部值。RefCell<T>:用于非Copy类型,提供经过运行时检查的可变/不可变引用。- 违反
RefCell的规则会导致运行时崩溃,从而确保安全性。
这些类型对于模拟对象、循环引用等模式非常重要,也适用于需要通过共享的不可变引用跟踪内部状态的场景。
常见问题解答
「内部可变性:RefCell、Cell」课时是免费的吗?
是的 — 「内部可变性:RefCell、Cell」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。
「内部可变性:RefCell、Cell」这节课中我会学到什么?
学习使用 `RefCell` 和 `Cell` 实现内部可变性,从而安全地通过不可变引用访问可变数据。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 3 节。
「内部可变性:RefCell、Cell」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Box、Rc 与 Arc 智能指针
- 内部可变性:RefCell、Cell
- 使用线程实现无畏并发