借用、可变借用与生命周期
本章目标
这一章学习如何在不转移所有权的情况下使用值。你会理解不可变借用、可变借用、引用不能悬空,以及生命周期标注真正想表达什么。
它是什么
借用就是临时使用别人的值。不可变借用用 &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,这样函数更通用。
练习
- 写一个函数
len(text: &str) -> usize。 - 写一个函数
clear(text: &mut String)。 - 在同一作用域中先创建不可变引用,再尝试可变引用,观察报错。
- 把不可变引用的使用提前结束,再尝试可变引用。
造轮子任务
写一个文本缓冲区 TextBuffer,内部保存 String。实现 as_str(&self) -> &str 和 push_line(&mut self, line: &str)。目标是体验只读借用和可变借用如何成为 API 的一部分。
小结
借用让代码不用到处转移所有权。生命周期让引用不会悬空。Rust 的借用检查不是为了为难你,而是在编译期阻止“有人读时另一个人乱改”“引用指向已经释放的值”。