← [ BACK_TO_NOTES ]

Rust 内部可变性

在共享引用下安全变更:Cell 值替换与 RefCell 运行时借用检查;与 Mutex 的单线程/多线程分工。

#concept #growing #dev #rust

[!info] related notes

Rust 内部可变性

这个概念为什么出现

有时 API 只能拿到 &self,但仍需变更内部缓存/计数。
内部可变性允许共享引用下通过受控 API 修改内部值。RefCell 用运行时借用检查;Cell 通过值替换避免暴露冲突引用;并发场景还可用原子或锁,并非所有内部可变性都依赖借用计数。

[!abstract] 一句话理解 RefCell 的 borrow / borrow_mut 返回 Ref / RefMut 守卫,守卫解引用后访问数据;冲突时 panic,try_borrow 系列则返回错误。Cell 的 get 需要 Copy,set/replace 可用于非 Copy 值。

最小可运行示例

场景:只读服务门面下的命中计数

配置服务对外 &self 查询,内部统计命中次数。

use std::cell::RefCell;

// 业务意图:不可变方法中更新计数。
// 教学点:RefCell borrow_mut;运行时检查。

struct ConfigService {
    hits: RefCell<u64>,
    region: String,
}

impl ConfigService {
    fn region(&self) -> &str {
        let mut hits = self.hits.borrow_mut();
        *hits = hits.saturating_add(1); // 示例计数达到上限后保持上限
        &self.region
    }
    fn hits(&self) -> u64 { *self.hits.borrow() }
}

fn main() {
    let svc = ConfigService { hits: RefCell::new(0), region: String::from("cn") };
    println!("region={}", svc.region());
    println!("hits={}", svc.hits());
}

建议运行:cargo run

期望:

region=cn
hits=1

结合场景再看三个关注点

  1. svc 本身无需 mut
  2. 嵌套 borrow 冲突会 panic
  3. 多线程用 Mutex 而非 RefCell

核心概念与准确模型

类型场景
Cell<T>Copy 或替换
RefCell<T>单线程动态借用
Mutex/RwLock多线程
std::cell::OnceCell / LazyCell单线程一次初始化 / 惰性初始化
std::sync::OnceLock / LazyLock满足相应类型约束时可跨线程共享的初始化容器

设计动机

  • 表达“逻辑共享但内部记账”
  • 保持外部 API 简洁
  • 仍有安全兜底(panic 而非 UB)

边界与误区

  • 把 RefCell 当默认 → 隐藏别名可变复杂度
  • Ref/RefMut 活跃时再请求冲突借用会失败;尤其避免持有守卫调用未知回调或跨 await
  • 运行时检查只管理 RefCell 内部借用,不能绕过引用生命周期检查
  • 与 Rc 组合形成图时的环

[!warning] 常见误区:RefCell 让程序在多线程自动安全 RefCell 不实现 Sync,不能经共享引用无保护地跨线程使用;T: Send 时它可以 Send,转移所有权到另一个线程与共享它是不同问题。

工程实践

  1. 优先重构所有权
  2. 计数/缓存等局部用内部可变
  3. 测试覆盖双 borrow 路径
  4. 多线程明确换锁

本节总结

  • 内部可变允许通过共享引用受控地修改内部状态,运行时借用检查只是实现方式之一
  • Cell/RefCell/Mutex 分工
  • 慎用但有价值

自测题

  1. RefCell 违反借用规则后果?
  2. 为何常与 Rc 组合?
参考答案
  1. borrow / borrow_mut 冲突时 panic;try_borrow / try_borrow_mut 返回 Err。
  2. 共享所有权节点上需要可变子字段时。

延伸阅读与资料来源

资料类型支撑内容
The Book — RefCell官方书内部可变性
std::cell标准库Cell 模块
创建于 2026/7/15 更新于 2026/10/7
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本