& 的各种用法

1. 不可变引用(借用)

最基本的形式,用于借用所有权而不转移:

fn print_length(s: &String) {
    println!("{}", s.len());
} // s 在这里归还,不影响原数据

let s = String::from("hello");
print_length(&s);
println!("{}", s); // 还能用

2. 可变引用 &mut

需要修改借用内容时使用,同一时间只能有一个可变引用

fn append(s: &mut String) {
    s.push_str(" world");
}

let mut s = String::from("hello");
append(&mut s);

3. 解引用 *

引用存的是地址,* 取值:

let x = 5;
let r = &x;
assert_eq!(*r, 5); // 解引用得到值

4. 自动解引用

Rust 会隐式解引用,让代码简洁:

let s = String::from("hello");
let r = &s;
// 等价于 (*r).len()
let len = r.len();

5. 模式匹配中的引用

不想转移所有权时:

let v = vec![1, 2, 3];
// 不获取所有权,只借用
for &x in &v {
    println!("{}", x);
}
// v 还能用

for 循环中 & 的智能匹配

Rust 的 for 循环会自动尝试解引用,这是写法的对比:

let v = vec![1, 2, 3];

// 方式1: item 是 &i32
for item in &v {
    println!("{}", *item); // 需要显式解引用
}

// 方式2: 使用 &x 模式,直接得到 i32
for &x in &v {
    println!("{}", x); // x 已经是 i32
}

对于可变引用:

let mut v = vec![1, 2, 3];

// 修改元素
for x in &mut v {
    *x *= 2; // 必须显式解引用才能修改
}

// 或者用 &mut 模式解构
for &mut x in &mut v {
    x *= 2; // x 是 i32,但不能这样修改原值!
}
// 注意:&mut x 解构后得到的 x 是副本,修改不影响原数组

.iter() vs & 的区别:

// 这两者是等价的
for x in v.iter()     // 显式调用 iter()
for x in &v           // 隐式调用 iter()

// 这两者也等价
for x in v.iter_mut()
for x in &mut v

6. 字面量前加 &

直接在字面量前加 & 创建引用,无需先绑定变量:

// 引用整数
let r: &i32 = &42;

// 引用字符串字面量
let s: &&str = &"hello";

// 引用数组
let arr: &[i32; 3] = &[1, 2, 3];

实用场景:函数参数期望引用时直接传递:

fn print_ref(x: &i32) {
    println!("{}", x);
}

print_ref(&100); // 直接传字面量引用,无需 let x = 100;

重要限制:字面量无法获取可变引用 &mut

// 编译错误!字面量是临时值,无法修改
// let r: &mut i32 = &mut 42;

// 正确做法:先绑定到可变变量
let mut x = 42;
let r: &mut i32 = &mut x;
*r = 100;

字符串字面量本身就是 &str(静态生命周期),再 & 就是 &&str

let s1: &str = "hello";       // 字符串切片,类型 &str
let s2: &&str = &"hello";     // 对字符串切片的引用

7. 解构赋值

let (x, y) = (&1, &2);
let &a = x; // a = 1

7. 闭包捕获

let s = String::from("hello");
// 闭包借用 s,不转移所有权
let f = || println!("{}", &s);
f();
println!("{}", s); // s 还在

关键规则

规则说明
任意数量不可变引用 OR 一个可变引用不能同时拥有
引用必须有效不能悬垂引用
&T 实现 Copy引用本身可以复制