掌握Rust的错误处理哲学、Result类型、?操作符、自定义错误类型和thiserror/anyhow库。
Rust没有异常!错误处理分为两类:
Result<T, E> — 文件不存在、网络超时等panic! — bug、越界访问、逻辑错误等编译器强制你处理Result,不会遗漏错误!
enum Result<T, E> {
Ok(T),
Err(E),
}
use std::fs;
use std::io;
fn read_file(path: &str) -> Result<String, io::Error> {
fs::read_to_string(path)
}
fn main() {
match read_file("hello.txt") {
Ok(content) => println!("内容: {}", content),
Err(e) => println!("错误: {}", e),
}
}
✅ 编译验证通过
?是Rust错误传播的语法糖——如果是Ok则解包,Err则提前返回:
use std::fs::File;
use std::io::{self, Read};
// 不用?的写法
fn read_username_old() -> Result<String, io::Error> {
let f = File::open("username.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
// 用?的写法——简洁得多
fn read_username() -> Result<String, io::Error> {
let mut s = String::new();
File::open("username.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 更简洁的写法
fn read_username_v3() -> Result<String, io::Error> {
std::fs::read_to_string("username.txt")
}
✅ 编译验证通过
use std::fmt;
enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError,
NotFound(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(e) => write!(f, "IO错误: {}", e),
AppError::Parse(e) => write!(f, "解析错误: {}", e),
AppError::NotFound(s) => write!(f, "未找到: {}", s),
}
}
}
impl std::error::Error for AppError {}
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}
impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
}
fn main() {
let ok: Result<i32, &str> = Ok(42);
let err: Result<i32, &str> = Err("failed");
// map / map_err
let r = ok.map(|x| x * 2); // Ok(84)
let r2 = err.map_err(|e| e.to_uppercase()); // Err("FAILED")
// and_then
let r3 = ok.and_then(|x| if x > 0 { Ok(x) } else { Err("neg") });
// unwrap_or / unwrap_or_else / unwrap_or_default
let v = err.unwrap_or(0);
let v2 = err.unwrap_or_else(|e| { println!("{}",e); 0 });
let v3: i32 = err.unwrap_or_default();
// is_ok / is_err
println!("ok? {} err? {}", ok.is_ok(), err.is_err());
}
✅ 编译验证通过
fn main() {
// panic! — 不可恢复,程序崩溃
// panic!("出大事了!");
// unwrap — None/Err时panic
let v: Option<i32> = None;
// v.unwrap(); // panic!
// expect — 带消息的unwrap
// v.expect("值不应该为None");
// panic时可以设置是否展开栈
// Cargo.toml: [profile.release] panic = "abort"
}
用?实现:读文件→解析整数→计算平方→写入结果。
为计算器实现自定义错误类型,支持除零、溢出、语法错误。
练习ok_or、ok、err、transpose方法。
掌握Rust错误处理——编译器保证不遗漏错误
以下资源帮助你深入学习本课主题:
本课涉及的核心概念,确保你理解每一个:
| 术语 | 说明 |
|---|---|
| 所有权 | Rust内存管理的核心机制,每个值有唯一所有者 |
| 借用 | 通过引用访问数据,不获取所有权 |
| 生命周期 | 引用有效的范围,编译期分析工具 |
| trait | Rust的接口/抽象机制,类似其他语言的接口 |
| 泛型 | 参数化类型,零成本抽象 |
| 模式匹配 | 强大的数据解构和分支机制 |
| 零成本抽象 | 高层抽象不引入运行时开销 |
| fearless concurrency | 编译器保证线程安全 |
Q: Rust学习曲线真的很陡吗?
A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。
Q: Rust适合什么项目?
A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。
Q: 遇到编译错误怎么办?
A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。