编程Rust 后端开发者

解释一下 Rust 中关于不可变 (immutable) 和可变 (mutable) 数据结构的工作原理,以及何时需要可变性?与此相关的实际限制是什么,如何才能合理地规避这些限制?

用 Hintsage AI 助手通过面试

答案。

在 Rust 中,数据结构默认是不可变的。这意味着,任何未使用 mut 关键字声明的变量或引用,在初始化后都无法更改。

let mut value = 10; // 可变变量 value += 5; let value2 = 10; // 不可变变量 // value2 += 5; // 编译错误:cannot assign twice to immutable variable

相同的逻辑适用于结构体的字段:

struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK,因为 pt 被声明为 mut

然而,可变性只影响变量的“顶部级别”:如果结构体通过不可变引用存储,则无法更改其数据,即使它们在结构体内部被声明为 mut。

另一个规避限制的方法是使用特定类型,比如 RefCell<T> 或者原子容器。这允许在表面上看似不可变的容器内修改数据,通过**“内部可变性” (interior mutability)**,例如:

use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // 安全,尽管没有 mut

隐含问题。

问题: 如果结构体字段显式声明为 mut,而自身变量未声明为 mut,能否修改该字段的值?

典型错误答案: 是的,如果字段声明为 mut,可以进行修改。

正确答案: 关键字 mut 不能在结构体字段声明时使用。可变性仅适用于变量本身或获得的引用。要修改结构体字段,必须将变量声明为 mut,或者使用“内部可变性”类型(例如,RefCell)。

示例:

struct Foo { val: i32 } // 字段未声明为 mut let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // 错误!变量不可变

由于不了解主题细微之处而导致的实际错误示例。


故事

在一个大型项目中,其中一名开发者试图修改结构体的字段,认为字段声明为 mut("struct S { mut x: i32 }")会带来所需的可变性。结果编译失败,重构过程拖延了几个小时,直到有人解释可变性是变量所有权的特性,而不是结构体的特性。


故事

在一个多线程相关的项目中,所有数据访问控制逻辑都存放在一个全局静态结构中。由于对通过 RefCell 的“内部可变性”的潜在能力不了解,团队编写了复杂的锁和竞争机制,而不是通过 Arc<Mutex<T>>RwLock<T> 来解决问题。


故事

开发者忘记在函数签名中将参数声明为 mut,导致函数无法修改传递的结构,结果在生产环境中只出现了这个bug(数据在函数调用后未更新)。了解默认传递是通过不可变引用可以让他在早期阶段避免错误。