0Pricing
Learn Rust Coding · 课时

内部可变性: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 反馈 — 无需本地设置。

此课程中的所有课时

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