跳到主要内容

RefCell 智能指针

RefCell<T> 提供内部可变性,允许在不可变引用存在的情况下修改数据。它在运行时而不是编译时检查借用规则。

什么是 RefCell?

RefCell<T> 是一个智能指针,它:

  • 提供内部可变性
  • 运行时借用检查
  • 单线程使用
  • 可以在不可变的情况下修改内容

基本用法

创建和使用 RefCell

use std::cell::RefCell;

fn main() {
let data = RefCell::new(5);

// 不可变借用
{
let borrowed = data.borrow();
println!("值: {}", *borrowed);
} // borrowed 在这里被释放

// 可变借用
{
let mut borrowed_mut = data.borrow_mut();
*borrowed_mut += 10;
println!("修改后的值: {}", *borrowed_mut);
} // borrowed_mut 在这里被释放

println!("最终值: {}", *data.borrow());
}

借用规则检查

use std::cell::RefCell;

fn main() {
let data = RefCell::new(vec![1, 2, 3]);

// 可以有多个不可变借用
let borrow1 = data.borrow();
let borrow2 = data.borrow();
println!("借用1: {:?}", *borrow1);
println!("借用2: {:?}", *borrow2);
drop(borrow1);
drop(borrow2);

// 只能有一个可变借用
let mut borrow_mut = data.borrow_mut();
borrow_mut.push(4);
println!("可变借用: {:?}", *borrow_mut);
drop(borrow_mut);

// 运行时 panic 示例(注释掉避免程序崩溃)
// let _borrow = data.borrow();
// let _borrow_mut = data.borrow_mut(); // 这会 panic!
}

内部可变性模式

不可变结构体中的可变字段

use std::cell::RefCell;

struct Counter {
value: RefCell<i32>,
}

impl Counter {
fn new() -> Self {
Counter {
value: RefCell::new(0),
}
}

fn increment(&self) { // 注意:这里是 &self,不是 &mut self
let mut val = self.value.borrow_mut();
*val += 1;
}

fn get(&self) -> i32 {
*self.value.borrow()
}
}

fn main() {
let counter = Counter::new();

counter.increment();
counter.increment();
counter.increment();

println!("计数器值: {}", counter.get());

// 即使 counter 是不可变的,我们仍然可以修改内部值
let immutable_counter = &counter;
immutable_counter.increment();
println!("通过不可变引用修改后: {}", immutable_counter.get());
}

缓存计算结果

use std::cell::RefCell;

struct ExpensiveCalculator {
cache: RefCell<Option<i32>>,
input: i32,
}

impl ExpensiveCalculator {
fn new(input: i32) -> Self {
ExpensiveCalculator {
cache: RefCell::new(None),
input,
}
}

fn calculate(&self) -> i32 {
// 检查缓存
if let Some(cached) = *self.cache.borrow() {
println!("使用缓存结果");
return cached;
}

// 执行昂贵的计算
println!("执行昂贵的计算...");
std::thread::sleep(std::time::Duration::from_millis(100));
let result = self.input * self.input;

// 缓存结果
*self.cache.borrow_mut() = Some(result);
result
}
}

fn main() {
let calculator = ExpensiveCalculator::new(10);

println!("第一次计算: {}", calculator.calculate());
println!("第二次计算: {}", calculator.calculate());
println!("第三次计算: {}", calculator.calculate());
}

RefCell 与 Rc 结合

共享可变数据

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
}

impl Node {
fn new(value: i32) -> Rc<Self> {
Rc::new(Node {
value,
children: RefCell::new(Vec::new()),
})
}

fn add_child(&self, child: Rc<Node>) {
self.children.borrow_mut().push(child);
}

fn get_children(&self) -> Vec<Rc<Node>> {
self.children.borrow().clone()
}
}

fn main() {
let root = Node::new(1);
let child1 = Node::new(2);
let child2 = Node::new(3);

root.add_child(Rc::clone(&child1));
root.add_child(Rc::clone(&child2));

// 添加孙子节点
child1.add_child(Node::new(4));
child1.add_child(Node::new(5));

println!("根节点值: {}", root.value);
println!("子节点数量: {}", root.get_children().len());

for (i, child) in root.get_children().iter().enumerate() {
println!("子节点 {}: 值={}, 子节点数={}",
i, child.value, child.get_children().len());
}
}

观察者模式实现

use std::rc::Rc;
use std::cell::RefCell;

trait Observer {
fn update(&self, message: &str);
}

struct ConcreteObserver {
name: String,
}

impl ConcreteObserver {
fn new(name: String) -> Self {
ConcreteObserver { name }
}
}

impl Observer for ConcreteObserver {
fn update(&self, message: &str) {
println!("{} 收到通知: {}", self.name, message);
}
}

struct Subject {
observers: RefCell<Vec<Rc<dyn Observer>>>,
state: RefCell<String>,
}

impl Subject {
fn new() -> Self {
Subject {
observers: RefCell::new(Vec::new()),
state: RefCell::new(String::new()),
}
}

fn attach(&self, observer: Rc<dyn Observer>) {
self.observers.borrow_mut().push(observer);
}

fn detach(&self, observer: &Rc<dyn Observer>) {
let mut observers = self.observers.borrow_mut();
observers.retain(|obs| !Rc::ptr_eq(obs, observer));
}

fn set_state(&self, state: String) {
*self.state.borrow_mut() = state.clone();
self.notify(&state);
}

fn notify(&self, message: &str) {
for observer in self.observers.borrow().iter() {
observer.update(message);
}
}

fn get_state(&self) -> String {
self.state.borrow().clone()
}
}

fn main() {
let subject = Subject::new();

let observer1: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者1".to_string()));
let observer2: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者2".to_string()));
let observer3: Rc<dyn Observer> = Rc::new(ConcreteObserver::new("观察者3".to_string()));

subject.attach(Rc::clone(&observer1));
subject.attach(Rc::clone(&observer2));
subject.attach(Rc::clone(&observer3));

subject.set_state("状态1".to_string());

// 移除一个观察者
subject.detach(&observer2);

subject.set_state("状态2".to_string());

println!("当前状态: {}", subject.get_state());
}

安全使用 RefCell

try_borrow 方法

use std::cell::RefCell;

fn main() {
let data = RefCell::new(vec![1, 2, 3]);

// 安全的借用检查
match data.try_borrow() {
Ok(borrowed) => {
println!("成功借用: {:?}", *borrowed);
}
Err(e) => {
println!("借用失败: {:?}", e);
}
}

// 安全的可变借用检查
match data.try_borrow_mut() {
Ok(mut borrowed) => {
borrowed.push(4);
println!("成功可变借用并修改: {:?}", *borrowed);
}
Err(e) => {
println!("可变借用失败: {:?}", e);
}
}

// 演示借用冲突检测
let _borrow = data.borrow();
match data.try_borrow_mut() {
Ok(_) => println!("意外成功"),
Err(e) => println!("预期的借用冲突: {:?}", e),
}
}

借用作用域管理

use std::cell::RefCell;

struct SafeContainer {
data: RefCell<Vec<i32>>,
}

impl SafeContainer {
fn new() -> Self {
SafeContainer {
data: RefCell::new(Vec::new()),
}
}

fn add_item(&self, item: i32) -> Result<(), &'static str> {
match self.data.try_borrow_mut() {
Ok(mut data) => {
data.push(item);
Ok(())
}
Err(_) => Err("无法获取可变借用"),
}
}

fn get_items(&self) -> Result<Vec<i32>, &'static str> {
match self.data.try_borrow() {
Ok(data) => Ok(data.clone()),
Err(_) => Err("无法获取不可变借用"),
}
}

fn process_with_callback<F>(&self, callback: F) -> Result<(), &'static str>
where
F: FnOnce(&mut Vec<i32>),
{
match self.data.try_borrow_mut() {
Ok(mut data) => {
callback(&mut data);
Ok(())
}
Err(_) => Err("无法获取可变借用"),
}
}
}

fn main() {
let container = SafeContainer::new();

// 安全地添加项目
container.add_item(1).unwrap();
container.add_item(2).unwrap();
container.add_item(3).unwrap();

// 安全地获取项目
match container.get_items() {
Ok(items) => println!("容器内容: {:?}", items),
Err(e) => println!("获取失败: {}", e),
}

// 使用回调处理数据
container.process_with_callback(|data| {
data.sort();
data.reverse();
}).unwrap();

println!("处理后内容: {:?}", container.get_items().unwrap());
}

性能考虑

RefCell 开销

use std::cell::RefCell;
use std::time::Instant;

fn main() {
let n = 1_000_000;

// 直接访问
let mut direct_data = 0;
let start = Instant::now();
for _ in 0..n {
direct_data += 1;
}
println!("直接访问时间: {:?}", start.elapsed());

// RefCell 访问
let refcell_data = RefCell::new(0);
let start = Instant::now();
for _ in 0..n {
*refcell_data.borrow_mut() += 1;
}
println!("RefCell 访问时间: {:?}", start.elapsed());

// 批量 RefCell 访问
let start = Instant::now();
{
let mut borrowed = refcell_data.borrow_mut();
for _ in 0..n {
*borrowed += 1;
}
}
println!("批量 RefCell 访问时间: {:?}", start.elapsed());

println!("RefCell 有运行时开销,但批量操作可以减少开销");
}

实际应用示例

配置管理器

use std::cell::RefCell;
use std::collections::HashMap;

struct ConfigManager {
config: RefCell<HashMap<String, String>>,
defaults: HashMap<String, String>,
}

impl ConfigManager {
fn new() -> Self {
let mut defaults = HashMap::new();
defaults.insert("debug".to_string(), "false".to_string());
defaults.insert("port".to_string(), "8080".to_string());
defaults.insert("host".to_string(), "localhost".to_string());

ConfigManager {
config: RefCell::new(HashMap::new()),
defaults,
}
}

fn set(&self, key: &str, value: &str) {
self.config.borrow_mut().insert(key.to_string(), value.to_string());
}

fn get(&self, key: &str) -> Option<String> {
// 先检查用户配置
if let Some(value) = self.config.borrow().get(key) {
return Some(value.clone());
}

// 再检查默认配置
self.defaults.get(key).cloned()
}

fn get_or_default(&self, key: &str, default: &str) -> String {
self.get(key).unwrap_or_else(|| default.to_string())
}

fn reset(&self, key: &str) {
self.config.borrow_mut().remove(key);
}

fn clear_all(&self) {
self.config.borrow_mut().clear();
}

fn dump_config(&self) -> HashMap<String, String> {
let mut result = self.defaults.clone();
for (key, value) in self.config.borrow().iter() {
result.insert(key.clone(), value.clone());
}
result
}
}

fn main() {
let config = ConfigManager::new();

// 使用默认值
println!("默认端口: {}", config.get("port").unwrap());
println!("默认调试: {}", config.get("debug").unwrap());

// 设置自定义值
config.set("port", "9000");
config.set("debug", "true");
config.set("database_url", "postgresql://localhost/mydb");

println!("自定义端口: {}", config.get("port").unwrap());
println!("自定义调试: {}", config.get("debug").unwrap());
println!("数据库URL: {}", config.get("database_url").unwrap());

// 获取不存在的键
println!("不存在的键: {:?}", config.get("nonexistent"));
println!("带默认值: {}", config.get_or_default("nonexistent", "default_value"));

// 重置配置
config.reset("port");
println!("重置后端口: {}", config.get("port").unwrap());

// 显示所有配置
println!("所有配置: {:?}", config.dump_config());
}

最佳实践

1. 何时使用 RefCell

// 适合使用 RefCell 的场景:
// - 需要内部可变性
// - 单线程环境
// - 借用检查在编译时无法满足
// - 缓存、配置管理

// 不适合的场景:
// - 多线程环境(使用 Mutex)
// - 可以用普通借用解决的情况
// - 性能敏感的代码

2. 避免运行时 panic

use std::cell::RefCell;

fn safe_refcell_usage() {
let data = RefCell::new(vec![1, 2, 3]);

// 好的做法:使用 try_borrow
if let Ok(borrowed) = data.try_borrow() {
println!("安全借用: {:?}", *borrowed);
}

// 避免:可能 panic 的借用
// let borrowed = data.borrow(); // 如果已经有可变借用会 panic
}

3. 最小化借用作用域

use std::cell::RefCell;

fn minimize_borrow_scope() {
let data = RefCell::new(vec![1, 2, 3]);

// 好的做法:最小化借用作用域
let len = {
let borrowed = data.borrow();
borrowed.len()
}; // borrowed 在这里被释放

// 现在可以安全地进行可变借用
data.borrow_mut().push(4);

println!("长度: {}", len);
}

RefCell 提供了强大的内部可变性,但需要谨慎使用以避免运行时错误。下一节我们将学习 Weak 智能指针。