实战总结
> 综合运用引用知识,通过实战示例巩固不可变引用、可变引用的使用场景与最佳实践。
完整示例
示例 1:数据统计
rust
▶ Runfn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
// 不可变借用 - 只读
let sum = calculate_sum(&numbers);
let avg = calculate_average(&numbers);
println!("总和:{},平均:{}", sum, avg);
// 可变借用 - 修改
double_all(&mut numbers);
println!("翻倍后:{:?}", numbers);
}
fn calculate_sum(nums: &Vec<i32>) -> i32 {
nums.iter().sum()
}
fn calculate_average(nums: &Vec<i32>) -> f64 {
let sum: i32 = nums.iter().sum();
*sum as f64 / nums.len() as f64
}
fn double_all(nums: &mut Vec<i32>) {
for num in nums.iter_mut() {
*num *= 2;
}
}示例 2:结构体修改器
rust
▶ Run#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
let mut person = Person {
name: String::from("Alice"),
age: 25,
};
// 修改名字
change_name(&mut person, "Bob");
// 过生日
celebrate_birthday(&mut person);
// 打印信息(只读)
print_info(&person);
}
fn change_name(person: &mut Person, new_name: &str) {
person.name = new_name.to_string();
}
fn celebrate_birthday(person: &mut Person) {
person.age += 1;
}
fn print_info(person: &Person) {
println!("姓名:{},年龄:{}", person.name, person.age);
}示例 3:字符串构建器
rust
▶ Runfn main() {
let mut buffer = String::with_capacity(100);
// 逐步构建字符串
append_line(&mut buffer, "第一行");
append_line(&mut buffer, "第二行");
append_line(&mut buffer, "第三行");
// 统计信息(只读)
let line_count = count_lines(&buffer);
let char_count = count_chars(&buffer);
println!("行数:{},字符数:{}", line_count, char_count);
println!("内容:\n{}", buffer);
}
fn append_line(buffer: &mut String, content: &str) {
buffer.push_str(content);
buffer.push('\n');
}
fn count_lines(buffer: &String) -> usize {
buffer.lines().count()
}
fn count_chars(buffer: &String) -> usize {
buffer.chars().count()
}常见错误
错误 1:忘记 mut
rust
▶ Runfn main() {
let s = String::from("hello");
// s.push_str(" world"); // ❌ 错误:需要 mut
let mut s = String::from("hello");
s.push_str(" world"); // ✅ 正确
}错误 2:可变引用使用时忘记 mut
rust
▶ Runfn modify(s: &String) {
// s.push_str(" world"); // ❌ 错误:需要&mut
}
fn modify_fixed(s: &mut String) {
s.push_str(" world"); // ✅ 正确
}错误 3:借用冲突
rust
▶ Runfn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // ❌ 错误
// 修复:分离使用
println!("{}", r1); // 先使用 r1
// 现在 r1 不再使用,可以借用 mut
let r2 = &mut s;
r2.push_str(" world");
println!("{}", r2);
}错误 4:返回悬垂引用
rust
▶ Run// ❌ 错误
// fn get_greeting() -> &String {
// let s = String::from("hello");
// &s
// }
// ✅ 正确:返回所有权
fn get_greeting() -> String {
String::from("hello")
}调试技巧
打印借用信息
rust
▶ Runfn main() {
let s = String::from("hello");
let r = &s;
println!("s 的地址:{:p}", &s);
println!("r 的地址:{:p}", &r);
println!("r 指向的地址:{:p}", r.as_ptr());
}跟踪借用
rust
▶ Runfn main() {
let mut s = String::from("hello");
println!("初始:{}", s);
{
let r = &mut s;
println!("借用中:{}", r);
r.push_str(" world");
} // 借用结束
println!("借用结束后:{}", s);
}练习
练习 1:不可变引用
创建函数 print_twice,接受字符串的引用并打印两次。
练习 2:可变引用
创建函数 clear_string,接受 String 的可变引用并清空它。
练习 3:借用规则实验
尝试以下代码,观察编译错误:
- 同时创建两个可变引用
- 同时创建可变和不可变引用
- 修复这些错误
小结
引用类型对比
| 类型 | 语法 | 数量 | 能否修改 | 用途 |
|---|---|---|---|---|
| 不可变引用 | &T | 任意 | ❌ | 只读访问 |
| 可变引用 | &mut T | 1 个 | ✅ | 修改数据 |
借用规则
同一时刻只能有一种借用
- 多个不可变引用,或
- 一个可变引用
引用不能比所有者存活更久
最佳实践
- 优先使用引用,避免不必要的所有权转移
- 使用 &str 而不是&String,更灵活
- 需要修改时使用&mut,否则使用不可变引用
- 借用范围尽量小,尽早释放借用
关键要点
- 引用是"借用",不获取所有权
- 借用规则防止数据竞争
- 编译器确保引用安全
- 自动解引用让代码更简洁
练习题
详见:练习题