错误处理
Rust 的错误哲学
Rust 将错误分为两类:
- 可恢复的错误 - 使用
Result<T, E> - 不可恢复的错误 - 使用
panic!
panic! 宏
panic! 用于遇到无法处理的错误时终止程序:
fn main() { panic!("程序发生了严重错误!崩溃!"); // 后续代码不会执行 }
常见的 panic 场景:
fn main() { let v = vec![1, 2, 3]; v[99]; // 索引越界 -> panic! }
Result 类型
Result<T, E> 是处理可恢复错误的标准方式:
enum Result<T, E> { Ok(T), // 成功 Err(E), // 失败 }
基础使用
use std::fs::File; use std::io::ErrorKind; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("创建文件失败: {:?}", e), }, other_error => { panic!("打开文件失败: {:?}", other_error) } }, }; }
unwrap 和 expect
简单快速的错误处理方式:
fn main() { // 成功则返回值,失败则 panic let f = File::open("hello.txt").unwrap(); // 类似 unwrap,但自定义 panic 信息 let f = File::open("hello.txt") .expect("无法打开 hello.txt"); }
? 运算符
? 运算符是错误传播的语法糖:成功则解包,失败则提前返回错误。
use std::fs::File; use std::io::{self, Read}; fn read_username_from_file() -> Result<String, io::Error> { let mut f = File::open("hello.txt")?; // 失败时 return Err let mut s = String::new(); f.read_to_string(&mut s)?; // 失败时 return Err Ok(s) } fn main() { match read_username_from_file() { Ok(name) => println!("用户名: {}", name), Err(e) => println!("读取失败: {}", e), } }
? 还可以链式调用:
fn read_username() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) }
Option 类型处理
Option<T> 用于值可能不存在的情况:
fn find_first_word(s: &str) -> Option<&str> { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return Some(&s[..i]); } } None // 没有空格返回 None } fn main() { match find_first_word("hello world") { Some(word) => println!("第一个词: {}", word), None => println!("没有找到空格"), } // Option 也可用 ? 运算符 fn try_find(s: &str) -> Option<&str> { let word = find_first_word(s)?; // None 则提前返回 Some(word) } }
组合子方法
Result 和 Option 提供了丰富的组合子方法:
fn main() { // map - 转换内部值 let result: Result<i32, &str> = Ok(5); let mapped = result.map(|x| x * 2); // Ok(10) // and_then - 链式操作 let result = result.and_then(|x| Ok(x + 3)); // Ok(8) // unwrap_or - 提供默认值 let default: Result<i32, &str> = Err("error"); let value = default.unwrap_or(0); // 0 // ok_or - Option 转 Result let option: Option<i32> = Some(5); let result = option.ok_or("值不存在"); // Ok(5) }
自定义错误类型
use std::fmt; #[derive(Debug)] enum MyError { IoError(std::io::Error), ParseError(String), } impl fmt::Display for MyError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { MyError::IoError(e) => write!(f, "IO 错误: {}", e), MyError::ParseError(s) => write!(f, "解析错误: {}", s), } } } impl std::error::Error for MyError {} // 实现标准 Error trait
本章实践
use std::io::{self, BufRead}; fn read_and_parse() -> Result<i32, String> { let mut line = String::new(); io::stdin().read_line(&mut line) .map_err(|e| format!("读取输入失败: {}", e))?; line.trim().parse::<i32>() .map_err(|_| "无法解析为整数".to_string()) } fn main() { println!("请输入一个整数:"); match read_and_parse() { Ok(n) => println!("你输入了: {}", n), Err(e) => println!("错误: {}", e), } }
本章小结
panic!用于不可恢复的错误Result<T, E>处理可恢复的错误unwrap和expect快速解包(失败时 panic)?运算符传播错误,写出简洁代码map、and_then、unwrap_or等组合子丰富处理方式- 自定义错误类型实现
Errortrait