Rust 学习笔记 06:引用与借用 (References & Borrowing) 下
“Borrow checkers are the strict librarians of the programming world.”
上一集我们讲了所有权 (Ownership):把东西给别人(Move),自己就没了。这虽然安全,但太麻烦了。
比如我想计算一个字符串长度,难道每次都要把所有权传进去,算完再传回来?
1// 痛苦的写法
2fn calculate_length(s: String) -> (String, usize) {
3 let length = s.len();
4 (s, length) // 还要把 s 传回去,心累
5}
Rust 说:别急,你可以 借 (Borrow) 啊。
1. 引用 (References)
引用允许你使用值但不获取其所有权。在 Rust 中,用 & 符号表示。
1fn main() {
2 let s1 = String::from("hello");
3
4 // 传引用:&s1
5 let len = calculate_length(&s1);
6
7 // s1 依然有效!因为我们只是借出去了,没送出去。
8 println!("The length of '{}' is {}.", s1, len);
9}
10
11// 接收引用:&String
12fn calculate_length(s: &String) -> usize {
13 s.len()
14}
这叫 借用 (Borrowing)。就像你借了朋友的书,你看完了得还回去(离开作用域),但书还是朋友的。
2. 可变引用 (Mutable References)
借来的书能乱涂乱画吗?默认不行(引用默认不可变)。
如果非要改,得申请 可变引用 (&mut)。
1fn main() {
2 let mut s = String::from("hello");
3
4 change(&mut s);
5 println!("{}", s); // hello, world
6}
7
8fn change(some_string: &mut String) {
9 some_string.push_str(", world");
10}
3. 借用规则 (The Rules)
这是 Rust 萌新最容易撞墙的地方。借用规则非常严格,为了防止 数据竞争 (Data Race)。
规则一:一山不容二虎(可变引用) 在特定作用域内,对某一块数据,只能有 一个 可变引用。
1let mut s = String::from("hello");
2let r1 = &mut s;
3let r2 = &mut s; // 报错!
4// 理由:r1 正在改 s,r2 也要改 s,容易打架。
规则二:读写互斥 你不能在拥有不可变引用(读锁)的同时,拥有可变引用(写锁)。
1let mut s = String::from("hello");
2let r1 = &s; // 我在读
3let r2 = &s; // 我也在读,没问题
4let r3 = &mut s; // 报错!
5// 理由:r1 和 r2 以为数据不会变,结果 r3 偷偷改了,r1/r2 就读到脏数据了。
总结为一句话:
要么有很多个只读的(&T),要么只能有一个可写的(&mut T)。
4. 悬垂引用 (Dangling References)
在 C++ 中,你可能会不小心返回一个栈上局部变量的指针,导致指针指向无效内存。 在 Rust 中,编译器会直接教你做人。
1fn dangle() -> &String {
2 let s = String::from("hello");
3 &s // 报错:s 离开作用域就释放了,你返回个寂寞?
4}
解决方法:直接返回 String(转移所有权),而不是引用。
5. 小结
第六篇笔记,我们学会了如何"借书"。
&是借(引用),&mut是不仅借还要改(可变引用)。- 规则:多读 或 一写,绝不妥协。
这两章(Ownership + Borrowing)是 Rust 最难也是最精彩的部分。跨过这道坎,剩下的就是星辰大海。
下一篇,我们将介绍一种特殊的引用 —— Slice (切片)。它不仅是不持有所有权的引用,而且引用的是一段连续的内存序列。
练习题:
- 尝试编写一段代码,违反"读写互斥"规则,看看编译器具体的报错信息。
- 思考:为什么 Rust 允许同时存在多个不可变引用?这会有线程安全问题吗?
思考题:
NLL (Non-Lexical Lifetimes) 是 Rust 2018 引入的特性。它如何让借用检查变得更聪明?(提示:观察文中的代码注释 r1, r2 作用域在这里结束)。
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
