引用与借用 #
一、引用概述 #
引用允许你使用值但不获取其所有权。创建引用的行为称为借用。
1.1 基本引用 #
rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 传递引用
println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s 离开作用域,但因为它只是引用,不会释放数据
1.2 借用的含义 #
- 借用:临时使用值,不获取所有权
- 引用:指向数据的指针
- 借用结束后,数据仍然属于原所有者
二、不可变引用 #
2.1 创建不可变引用 #
使用 & 创建不可变引用:
rust
fn main() {
let s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &s; // 另一个不可变引用
println!("{} {}", r1, r2);
println!("原值: {}", s);
}
2.2 多个不可变引用 #
可以同时存在多个不可变引用:
rust
fn main() {
let s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("{} {} {}", r1, r2, r3);
}
2.3 引用的作用域 #
引用的作用域从声明开始,到最后一次使用结束:
rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
println!("r1: {}", r1); // r1 最后一次使用
// r1 作用域结束
let r2 = &mut s; // 正确:r1 已不再使用
println!("r2: {}", r2);
}
三、可变引用 #
3.1 创建可变引用 #
使用 &mut 创建可变引用:
rust
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
3.2 可变引用的限制 #
同一时刻只能有一个可变引用:
rust
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // 错误:不能同时有两个可变引用
println!("{}", r1);
}
3.3 可变引用与不可变引用 #
可变引用存在时,不能有不可变引用:
rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
// let r3 = &mut s; // 错误:已有不可变引用
println!("{} {}", r1, r2);
}
3.4 非重叠作用域 #
引用的作用域可以不重叠:
rust
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
println!("{}", r1);
} // r1 作用域结束
let r2 = &mut s; // 正确
println!("{}", r2);
}
四、借用规则 #
4.1 核心规则 #
Rust的借用规则:
- 任意时刻,要么只能有一个可变引用,要么只能有多个不可变引用
- 引用必须始终有效(不能有悬垂引用)
4.2 规则图解 #
text
┌─────────────────────────────────────────┐
│ 借用规则 │
├─────────────────────────────────────────┤
│ 可以:多个不可变引用 │
│ ┌───┐ ┌───┐ ┌───┐ │
│ │&T │ │&T │ │&T │ ──→ 数据 │
│ └───┘ └───┘ └───┘ │
├─────────────────────────────────────────┤
│ 可以:一个可变引用 │
│ ┌───────┐ │
│ │&mut T │ ──→ 数据 │
│ └───────┘ │
├─────────────────────────────────────────┤
│ 不可以:可变引用 + 不可变引用 │
│ ┌───┐ ┌───────┐ │
│ │&T │ │&mut T │ ──→ 数据 ✗ 错误 │
│ └───┘ └───────┘ │
└─────────────────────────────────────────┘
4.3 为什么有这些规则 #
防止数据竞争,数据竞争发生在以下情况:
- 两个或多个指针同时访问同一数据
- 至少有一个指针在写入
- 没有同步机制
五、悬垂引用 #
5.1 Rust 防止悬垂引用 #
Rust编译器确保引用永远有效:
rust
fn main() {
let reference_to_nothing = dangle();
println!("{}", reference_to_nothing);
}
// 错误示例
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 返回悬垂引用
// } // s 在这里被释放
// 正确做法
fn dangle() -> String {
let s = String::from("hello");
s // 返回所有权
}
5.2 编译器错误信息 #
text
error[E0106]: missing lifetime specifier
--> src/main.rs:1:16
|
1 | fn dangle() -> &String {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value,
but there is no value for it to be borrowed from
六、引用与函数 #
6.1 传递引用 #
rust
fn main() {
let s = String::from("hello");
print_length(&s);
println!("s 仍然有效: {}", s);
}
fn print_length(s: &String) {
println!("长度: {}", s.len());
}
6.2 返回引用 #
返回引用需要确保引用的数据有效:
rust
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let longer = longest(&s1, &s2);
println!("更长的字符串: {}", longer);
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
6.3 修改借用的值 #
rust
fn main() {
let mut s = String::from("hello");
append_world(&mut s);
println!("{}", s);
}
fn append_world(s: &mut String) {
s.push_str(", world");
}
七、解引用 #
7.1 解引用运算符 #
使用 * 解引用:
rust
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
println!("x = {}, *y = {}", x, *y);
}
7.2 自动解引用 #
Rust会自动解引用:
rust
fn main() {
let s = String::from("hello");
let r = &s;
// 自动解引用
println!("长度: {}", r.len()); // 等价于 (*r).len()
println!("内容: {}", r); // 自动解引用
}
7.3 可变解引用 #
rust
fn main() {
let mut x = 5;
let y = &mut x;
*y += 1; // 解引用并修改
println!("x = {}", x);
}
八、引用与切片 #
8.1 字符串切片 #
rust
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("{} {}", hello, world);
}
8.2 数组切片 #
rust
fn main() {
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..3];
println!("{:?}", slice);
}
九、实践示例 #
9.1 查找字符串 #
rust
fn main() {
let text = String::from("hello world");
let word = first_word(&text);
println!("第一个单词: {}", word);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
9.2 修改结构体 #
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
}
fn main() {
let mut rect = Rectangle {
width: 10,
height: 20,
};
println!("面积: {}", rect.area());
rect.scale(2);
println!("缩放后面积: {}", rect.area());
}
9.3 安全的数据访问 #
rust
fn main() {
let mut data = vec![1, 2, 3, 4, 5];
// 安全地读取和修改
let first = &data[0];
println!("第一个元素: {}", first);
// first 作用域结束
data.push(6); // 正确:没有活跃的引用
println!("数据: {:?}", data);
}
十、常见错误 #
10.1 同时持有可变和不可变引用 #
rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
// s.push_str(" world"); // 错误:已有不可变引用
println!("{} {}", r1, r2);
}
10.2 返回局部变量的引用 #
rust
// 错误
// fn get_string() -> &String {
// let s = String::from("hello");
// &s // 返回局部变量的引用
// }
// 正确
fn get_string() -> String {
String::from("hello")
}
10.3 在迭代时修改集合 #
rust
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
// 错误:不能在迭代时修改
// for i in &v {
// v.push(*i + 1);
// }
// 正确:先收集,后修改
let to_add: Vec<i32> = v.iter().map(|&x| x + 1).collect();
v.extend(to_add);
println!("{:?}", v);
}
十一、总结 #
本章学习了:
- 引用与借用的概念
- 不可变引用
&T - 可变引用
&mut T - 借用规则
- 防止悬垂引用
- 解引用操作
- 引用与切片
引用和借用让Rust在不牺牲安全性的前提下实现高效的内存访问。下一章,我们将学习生命周期。
最后更新:2026-03-27