跳至内容
张叶安的小站
用户工具
登录
站点工具
搜索
工具
显示页面
过去修订
反向链接
最近更改
媒体管理器
网站地图
登录
>
最近更改
媒体管理器
网站地图
您的足迹:
rust:第十章错误处理
本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。
====== 第十章 错误处理 ====== ===== 10.1 Rust的错误处理哲学 ===== Rust将错误分为两类: * **可恢复错误**:用户可以处理的问题,如文件未找到 * **不可恢复错误**:bug,如数组越界访问 **对应机制:** * 可恢复错误 -> Result<T, E> * 不可恢复错误 -> panic! ===== 10.2 不可恢复错误与panic! ===== ==== 什么是panic ==== 当代码panic时,程序会: 1. 打印错误信息 2. 展开(unwind)调用栈,清理数据 3. 退出程序 ==== 触发panic ==== **显式调用:** <code rust> panic!("crash and burn"); </code> **隐式触发(bug):** <code rust> let v = vec![1, 2, 3]; v[99]; // panic!索引越界 </code> ==== panic时的行为 ==== 默认情况下,panic会展开调用栈: 1. Rust沿着调用栈回溯 2. 清理每个函数中的数据 3. 打印错误信息并退出 **设置panic为中止(abort):** 在Cargo.toml中: <code toml> [profile.release] panic = 'abort' </code> 这样panic时不会清理,直接退出,可减小二进制文件大小。 ==== 使用panic!宏 ==== <code rust> fn main() { let guess: i32 = "not a number".parse().expect("需要一个数字!"); // 或者 // let guess: i32 = "not a number".parse().unwrap(); } </code> ==== 环境变量控制backtrace ==== 设置环境变量查看完整的调用栈: <code bash> RUST_BACKTRACE=1 cargo run </code> 使用full获取更详细的信息: <code bash> RUST_BACKTRACE=full cargo run </code> ===== 10.3 可恢复错误与Result ===== ==== Result枚举 ==== <code rust> enum Result<T, E> { Ok(T), Err(E), } </code> **使用Result:** <code rust> use std::fs::File; let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => { println!("打开文件失败:{:?}", error); panic!("程序退出"); } }; </code> ==== 匹配不同错误 ==== <code rust> use std::fs::File; use std::io::ErrorKind; 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) } }, }; </code> ===== 10.4 快捷方法 ===== ==== unwrap ==== Ok时返回值,Err时panic: <code rust> let f = File::open("hello.txt").unwrap(); </code> ==== expect ==== unwrap的增强版,可自定义panic信息: <code rust> let f = File::open("hello.txt") .expect("hello.txt应该存在"); </code> **建议**:在真实项目中优先使用expect,提供有用的错误信息。 ==== unwrap_or和unwrap_or_else ==== 提供默认值: <code rust> let s = "123"; let n = s.parse::<i32>().unwrap_or(0); // 解析失败返回0 // 或使用闭包 let n = s.parse::<i32>().unwrap_or_else(|_| { println!("解析失败,使用默认值"); 0 }); </code> ===== 10.5 传播错误 ===== ==== 手动传播 ==== <code rust> use std::fs::File; use std::io::{self, Read}; fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("hello.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), } } </code> ==== ?运算符 ==== ?是传播错误的简写: <code rust> 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")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) } </code> ?的行为: * Ok时解开值 * Err时提前返回Err ==== ?与match等价 ==== <code rust> let mut f = match File::open("hello.txt") { Ok(file) => file, Err(e) => return Err(e), }; // 等价于 let mut f = File::open("hello.txt")?; </code> ==== 链式调用? ==== <code rust> use std::fs; use std::io; fn read_username_from_file() -> Result<String, io::Error> { fs::read_to_string("hello.txt") } </code> 更进一步简化: <code rust> use std::io; fn read_username_from_file() -> Result<String, io::Error> { std::fs::read_to_string("hello.txt") } </code> ===== 10.6 ?与Option ===== ?也可用于Option: <code rust> fn last_char_of_first_line(text: &str) -> Option<char> { text.lines().next()?.chars().last() } fn main() { assert_eq!( last_char_of_first_line("Hello, world\nHow are you"), Some('d') ); assert_eq!(last_char_of_first_line(""), None); assert_eq!(last_char_of_first_line("\nhi"), None); } </code> **混合使用Result和Option:** <code rust> use std::error::Error; fn parse_and_double(s: Option<&str>) -> Result<i32, Box<dyn Error>> { let s = s.ok_or("没有输入")?; let n: i32 = s.parse()?; Ok(n * 2) } </code> ===== 10.7 错误处理最佳实践 ===== ==== 何时使用panic ==== **应该panic的情况:** * 示例代码、原型代码、测试 * 你确定不可能发生的情况(如unreachable!()) * 损坏的状态无法恢复 <code rust> // 示例:unwrap在测试中可以接受 #[test] fn test_something() { let result = some_operation().unwrap(); assert_eq!(result, expected); } // 示例:不可能发生的情况 match some_enum { A => ..., B => ..., _ => unreachable!("枚举只有A和B两个变体"), } </code> **不应该panic的情况:** * 无效的用户输入 * 外部资源不可用 * 网络连接失败 ==== 错误信息 ==== 提供有用的错误信息: <code rust> // 好的做法 let config = Config::build(&args) .expect("解析参数失败"); // 更好的做法:传播错误让用户处理 let config = Config::build(&args)?; </code> ===== 练习题 ===== ==== 练习题10.1:安全除法 ==== <code rust> fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("除数不能为零")) } else { Ok(a / b) } } fn main() { match divide(10.0, 2.0) { Ok(result) => println!("结果:{}", result), Err(msg) => println!("错误:{}", msg), } match divide(10.0, 0.0) { Ok(result) => println!("结果:{}", result), Err(msg) => println!("错误:{}", msg), } } </code> ==== 练习题10.2:读取配置文件 ==== <code rust> use std::fs; use std::io; fn read_config(filename: &str) -> Result<String, io::Error> { fs::read_to_string(filename) } fn main() { match read_config("config.txt") { Ok(content) => println!("配置内容:\n{}", content), Err(e) => println!("读取配置失败:{}", e), } } </code> ==== 练习题10.3:级联错误处理 ==== <code rust> use std::fs::File; use std::io::{self, BufRead, BufReader}; fn read_numbers(filename: &str) -> Result<Vec<i32>, Box<dyn std::error::Error>> { let file = File::open(filename)?; let reader = BufReader::new(file); let mut numbers = Vec::new(); for line in reader.lines() { let line = line?; let num: i32 = line.parse()?; numbers.push(num); } Ok(numbers) } fn main() { match read_numbers("numbers.txt") { Ok(nums) => println!("数字:{:?}", nums), Err(e) => println!("错误:{}", e), } } </code> ===== 本章小结 ===== 本章学习了Rust的错误处理机制: * **panic!**:不可恢复错误,程序终止 * **Result<T, E>**:可恢复错误,Ok或Err * **unwrap/expect**:快捷处理,失败时panic * **?运算符**:传播错误的简洁语法 * **最佳实践**: * 可恢复错误用Result * 不可恢复错误用panic * 提供有用的错误信息 Rust的错误处理强制程序员处理所有可能的错误情况,使程序更健壮。
rust/第十章错误处理.txt
· 最后更改:
2026/02/03 19:45
由
127.0.0.1
页面工具
显示页面
过去修订
反向链接
回到顶部