章节目录

借用、可变借用与生命周期

本章目标

这一章学习如何在不转移所有权的情况下使用值。你会理解不可变借用、可变借用、引用不能悬空,以及生命周期标注真正想表达什么。

它是什么

借用就是临时使用别人的值。不可变借用用 &T,可变借用用 &mut T

fn print_title(title: &str) {
    println!("{title}");
}

这个函数只读标题,不拥有标题。

为什么需要

如果每次调用函数都移动所有权,程序会非常难写。借用允许多个函数读取同一份数据,而不复制它。

Rust 的借用规则核心是:

  • 可以有多个不可变借用。
  • 同一时间只能有一个可变借用。
  • 可变借用和不可变借用不能同时活跃。
  • 引用不能比它指向的值活得更久。

这些规则防止数据竞争和悬垂引用。

怎么使用

fn append_suffix(text: &mut String) {
    text.push_str("!");
}

let mut title = String::from("Rust");
append_suffix(&mut title);
println!("{title}");

调用者保留所有权,只把修改权限临时借给函数。

逐行解释

fn longer<'a>(left: &'a str, right: &'a str) -> &'a str {
    if left.len() >= right.len() {
        left
    } else {
        right
    }
}
  • fn longer 定义函数,返回两个字符串中更长的那个引用。
  • <'a> 声明一个生命周期参数。
  • left: &'a str 表示 left 引用至少在 'a 这段时间内有效。
  • right: &'a str 表示 right 也至少在 'a 这段时间内有效。
  • -> &'a str 表示返回值引用的有效期和输入引用有关。
  • 函数没有创建新字符串,只返回其中一个输入引用。
  • 生命周期标注不是延长生命,而是告诉编译器返回引用来自哪里。

常见坑

  • 生命周期不是让值活得更久,而是描述引用之间的关系。
  • 不要一开始到处写生命周期。很多场景编译器能自动推断。
  • 出现借用冲突时,先缩小引用使用范围,而不是急着 clone()
  • &String 通常可以写成 &str,这样函数更通用。

练习

  1. 写一个函数 len(text: &str) -> usize
  2. 写一个函数 clear(text: &mut String)
  3. 在同一作用域中先创建不可变引用,再尝试可变引用,观察报错。
  4. 把不可变引用的使用提前结束,再尝试可变引用。

造轮子任务

写一个文本缓冲区 TextBuffer,内部保存 String。实现 as_str(&self) -> &strpush_line(&mut self, line: &str)。目标是体验只读借用和可变借用如何成为 API 的一部分。

小结

借用让代码不用到处转移所有权。生命周期让引用不会悬空。Rust 的借用检查不是为了为难你,而是在编译期阻止“有人读时另一个人乱改”“引用指向已经释放的值”。