Rust 核心概念解析:引用、借用与内部可变性

Rust 核心概念解析:引用、借用与内部可变性

管理内存安全,特别是防止数据竞争和悬垂指针,是系统编程中的一个核心挑战。Rust 语言通过其所有权和借用检查系统,在编译阶段就为解决这些问题提供了强有力的保障。

本文聚焦于该系统的关键部分:引用。我们将详细解析共享引用 (&T) 与可变引用 (&mut T) 的工作原理与编译时规则,并进一步探讨"内部可变性"这一高级模式,它为特定场景提供了必要的灵活性。

引用及内部可变性

引用

  • 通过引用,Rust 允许将值借用出去,但不放弃所有权
  • 引用就是带有附加合约的指针

共享引用

  • &T,就是可以共享的指针:
    • 可同时存在任意数量的引用指向同一个值
    • 每个共享的引用都实现了 Copy
  • 背后的值不可变
    • 编译器允许假定共享引用指向的值,在该引用存活期间是不会改变的
    • 例如:一个共享引用的值在某函数内被多次读取,那编译器就有权让其只读取一次,然后重用读取的值。

可变引用

  • &mut T
  • 可变引用是独占的
    • 编译器假定没有其它线程访问目标值(无论是通过共享引用还是可变引用)
rust 复制代码
fn main() {
  println!("Hello, world!");
}

fn noalias(input: &i32, output: &mut i32) {
  if *input == 1 {
    *output = 2;
  }
  if  *input !=1 {
    *output = 3;
  }
}
  • 可变引用只允许你修改引用所指向的内存地址
rust 复制代码
fn main() {
  let x = 42;
  let mut y = &x; // y is of type &i32
  let z = &mut y; // z is of type &mut &i32
}

拥有值 VS 拥有到值的可变引用

  • 所有者需要对删除值(丢弃值)负责
  • 警告:如果你移动了可变引用背后的值,则必须在其位置上留下另一个值。如果不这样做,所有者会认为他需要将其删除(丢弃),但其实却没有值可以删除了。
rust 复制代码
fn main() {
  let mut s = Box::new(42);
  replace_with_84(&mut s);
}

fn replace_with_84(s: &mut Box<i32>) {
  // this is not okay, as *s would be empty;
  // let was = *s;
  // but this is:
  let was = std::mem::take(s);
  // so is this:
  *s = was;
  // we can exchange values behind &mut:
  let mut r = Box::new(84);
  std::mem::swap(s, &mut r);
  assert_ne!(*r, 84);
}

内部可变性

  • 一些类型提供了内部可变性:
    • 可通过共享引用修改值
  • 这些类型通常依赖于额外的机制(如原子 CPU 指令)或不变量来提供安全的可变性,而不依赖于独占引用的语义
  • 分为两类:
    • 通过共享引用获得可变引用
    • 通过共享引用可以替换值
  • 通过共享引用获得可变引用:Mutex、RefCell
    • 提供保障机制:针对任何提供了可变引用指向的值,同时只会存在一个可变引用(没有共享引用)
    • 依赖于 UnsafeCell 类型,通过共享引用修改值的唯一正确方式
  • 通过共享引用可以替换值:std::sync::atomic、std::cell::Cell
    • 没有提供可变引用到内部值
    • 提供就地操作值的方法
    • 例:无法获得到 usize 或 i32 的直接引用,但是可以读取和替换值

Cell 类型

  • 标准库
  • 通过不变量实现的内部可变性
    • 无法跨线程共享(内部值不会被并发的修改,即使通过共享引用发生修改)
    • 不会提供到 Cell 内部的值的引用(所以可以一直移动它)
  • 提供的方法:
    • 对值整体替换
    • 返回值的副本

总结

本文的核心在于 Rust 的引用系统,其安全性建立在两条基本原则之上:数据可以在多个共享引用 (&T) 之间只读共享,或者通过一个独占的可变引用 (&mut T) 进行修改。编译器的借用检查器严格实施这些规则,从而有效防止了数据竞争。

然而,为了在不牺牲安全的前提下提高灵活性,Rust 引入了"内部可变性"作为补充机制。像 Cell 等类型,将借用规则的检查从编译时推迟到运行时,允许在共享引用的访问范围内安全地修改数据。

因此,Rust 的内存安全模型是编译时静态检查和运行时动态检查的结合。理解这两种机制如何协同工作,是编写出高效且健壮的 Rust 代码的重要基础。

参考

相关推荐
程序员爱钓鱼34 分钟前
Go语言实战案例-创建模型并自动迁移
后端·google·go
javachen__39 分钟前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
uzong6 小时前
技术故障复盘模版
后端
GetcharZp7 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程7 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研7 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi7 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国8 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy9 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
草梅友仁9 小时前
草梅 Auth 1.4.0 发布与 ESLint v9 更新 | 2025 年第 33 周草梅周报
vue.js·github·nuxt.js