引言:为什么 Rust 看起来很难?
Rust 是一种现代系统编程语言,它能在没有垃圾收集器的情况下保证内存安全。然而,其严格的类型系统和独特的所有权模型常常让初学者感到困惑。如果你刚开始学习 Rust,并且不断看到编译器报出的红色错误信息,请不要气馁——这很正常。在本文中,我们将剖析 Rust 初学者最常犯的 10 个错误,并展示如何修复它们。
1. 忽略借用检查器
最常见的错误是在移动(move)后尝试使用某个值,或者创建了可变引用。Rust 编译器(借用检查器)严格执行所有权规则。
问题
let s1 = String::from("hello");let s2 = s1; // s1 移动到了 s2println!("{}", s1); // 错误!s1 不再拥有该数据解决方案
如果你需要复制数据,请使用 clone(),或者传递引用 &:
let s1 = String::from("hello");let s2 = &s1; // 借用println!("{} and {}", s1, s2); // 有效!提示: 如果编译器报错说所有权问题——你很可能需要的是一个引用,而不是完全移动。
2. 混淆 String 和 &str
初学者常常不理解拥有所有权的类型 String 和切片 &str 之间的区别。这会导致类型错误和不必要的内存分配。
问题
fn greet(name: &str) { println!("Hello, {}!", name);}
let my_name = String::from("Anna");greet(my_name); // 错误:期望 &str,但得到 String解决方案
传递一个指向 String 的引用——它会自动强制转换为 &str:
greet(&my_name); // 有效,得益于 Deref 强制转换对于拥有和修改字符串,使用 String;对于读取和传递给函数,使用 &str。
3. 过度使用 unwrap() 和 expect()
unwrap() 和 expect() 方法是处理 Option 或 Result 的快捷方式,但它们在出错时会导致程序恐慌(panic)。这在生产代码中是不可接受的。
问题
let number: Option<i32> = None;let value = number.unwrap(); // 程序恐慌!解决方案
使用 match 或者 unwrap_or、unwrap_or_else 等方法:
let value = number.unwrap_or(0); // 如果是 None,返回 0// 或者使用 matchmatch number { Some(v) => println!("{}", v), None => println!("值不存在"),}对于 Result,使用 ? 操作符将错误向上传播。
4. 错误地使用可变引用
“一个可变引用 或 多个不可变引用”这条规则经常被违反。
问题
let mut data = vec![1, 2, 3];let ref1 = &mut data;let ref2 = &mut data; // 错误!println!("{}", ref1);解决方案
使用花括号限制可变引用的作用域:
let mut data = vec![1, 2, 3];{ let ref1 = &mut data; ref1.push(4);} // ref1 离开作用域let ref2 = &mut data; // 现在可以了ref2.push(5);5. 忘记生命周期
当编译器要求显式指定生命周期时,初学者常常卡住。最常见的错误是返回对局部变量的引用。
问题
fn get_ref() -> &str { let s = String::from("test"); &s // 错误:返回对局部变量的引用}解决方案
要么返回拥有所有权的类型 String,要么接受一个引用作为参数:
博客
书籍推荐
推荐阅读