在 Rust 中,变量的可变性有两种核心方式:
使用
mut
关键字声明可变变量使用
RefCell<T>
提供内部可变性
本篇博客将通过对比和示例,讲解这两者的异同和适用场景。
一、使用mut
声明可变变量
这是最常规的可变变量声明方式,所有可变行为都必须在编译时通过检查:
fn main() {
let mut s = String::from("hello");
s.push_str(" world");
println!("{}", s); // 输出:hello world
}
特点:
编译器在编译时就保证你不会同时拥有可变引用和不可变引用
性能最优,但灵活性不高,尤其是在嵌套结构中
二、使用RefCell<T>
实现内部可变性
RefCell<T>
提供运行时的“内部可变性”:即使在不可变引用下,也可以对数据进行修改。
use std::cell::RefCell;
fn main() {
let data = RefCell::new(String::from("hello"));
data.borrow_mut().push_str(" world");
println!("{}", data.borrow());
}
你甚至可以在不可变借用中修改内容:
fn modify(data: &RefCell<String>) {
data.borrow_mut().push_str("!");
}
fn main() {
let s = RefCell::new(String::from("hello"));
modify(&s);
println!("{}", s.borrow()); // 输出:hello!
}
注意:如果在运行时违反了借用规则(比如两个可变引用或可变+不可变并存),RefCell 会 panic。
举个例子:
use std::cell::RefCell;
fn main() {
let data = RefCell::new(String::from("hello"));
let r1 = data.borrow(); // 不可变借用
let r2 = data.borrow_mut(); // 尝试可变借用 -> 会在运行时 panic!
println!("{}", r1);
}
运行时会报错:
thread 'main' panicked at 'already borrowed: BorrowMutError', src/main.rs:6
这是因为 Rust 的借用规则要求:
要么多个不可变引用
要么一个可变引用
不能两者并存,而 RefCell 在运行时动态检测这个约束。
三、两者对比总结
四、何时使用 RefCell<T>
?
结构体整体是不可变的,但某个字段需要动态修改;
多个组件需要共享数据,并在内部修改;
和 Rc<T> 配合,用于多所有者 + 内部可变;
use std::cell::RefCell;
use std::rc::Rc;
struct App {
state: Rc<RefCell<String>>,
}
fn main() {
let shared_state = Rc::new(RefCell::new(String::from("start")));
let app1 = App { state: Rc::clone(&shared_state) };
let app2 = App { state: Rc::clone(&shared_state) };
app1.state.borrow_mut().push_str(" - updated by app1");
app2.state.borrow_mut().push_str(" - updated by app2");
println!("最终状态: {}", shared_state.borrow());
}
五、小结
选择哪种方式取决于你的具体需求。如果能用 mut 解决问题,那就是最好的选择;若需要灵活性且场景符合 RefCell 的使用边界,那它是你构建复杂数据结构的强大工具。