模块课程(2/2)
错误处理和?操作员
Rust 的哲学鼓励您明确地认识到错误的可能性,并在程序编译之前构建代码来处理它们。
Rust 中的错误分为两大类:不可恢复的错误(通过 panic! 宏导致立即终止)和可恢复的错误(使用 Result<T, E> 类型管理)。
Result<T, E> 类型和模式匹配
大多数可恢复错误返回 Result<T, E> 类型,它是一个定义为的枚举:
enum Result<T, E> {
Ok(T),
Err(E),
}我们可以使用模式匹配来检查结果:
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}使用 ? 运算符进行错误传播
在实现函数时,我们通常希望将错误返回给调用者,以便他们决定要做什么,而不是直接在函数内部处理错误。这个过程称为错误传播。
Rust 提供 ? 运算符作为传播错误的语法快捷方式。如果 Result 的值为 Ok,则表达式返回 Ok 的内部值;如果是 Err,则从整个当前函数返回 Err 错误,就像我们使用了 return Err(...) 一样。
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("username.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}[!重要]
?运算符只能在返回与其所应用的值兼容的类型的函数中使用(通常为Result、Option或实现FromResidual的类型)。
自定义错误
我们可以通过实现 std::fmt::Display 特征(以及可选的 std::error::Error )来定义我们自己的自定义错误类型,以提供人类可读的错误表示:
use std::fmt;
#[derive(Debug)]
struct MyError {
details: String,
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error: {}", self.details)
}
}
impl std::error::Error for MyError {}自己尝试一下
练习 1:使用 ? 读取文件操作员
完成 read_username_from_file 函数,以便它使用 ?运算符传播通过打开文件并将其内容读入用户名字符串而生成的错误。
显示提示
在“File::open(...)”和“username_file.read_to_string(...)”之后添加“?”字符以传播错误。
3 次尝试后可用的解决方案
练习 2:解析数字并将其加倍
编写一个名为 parse_and_double 的函数,该函数接受字符串引用 &str 并返回 Result<i32, std::num::ParseIntError>。该函数应该使用 val.parse::<i32>() 和 ? 来解析字符串。传播解析错误,并在成功时返回包含在 Ok 中的双倍值。
显示提示
使用“val.parse::<i32>()?”解析字符串并传播错误,然后返回“Ok(num \* 2)”。
3 次尝试后可用的解决方案
练习 3:定义自定义错误
定义一个名为 CustomError 的空结构。通过在 fmt 方法中编写“Si e verificato un errore individualizzato”作为错误消息来实现它的 std::fmt::Display 特征。
显示提示
声明`struct CustomError;`并通过在`fmt`方法中编写`write!(f, "Si e verificato un errore individualizzato")`来实现Display。
3 次尝试后可用的解决方案