Rust 中有两种类型的错误:
- 不可恢复的错误(例如,未检查的越界数组访问)
- 可恢复的错误(例如,功能失败)
不可恢复的错误
对于无法处理且会使您的程序进入不可恢复状态的错误,我们使用panic! 宏。
fn encrypt(key: &[u8], data: &[u8]) -> Vec<u8> { if key.len() != 32 { panic!("encrypt: key length is invalid"); } // ... }
|
另一种触发 a 的方法panic是使用assert! 宏。
fn encrypt(key: &[u8], data: &[u8]) -> Vec<u8> { assert!(key.len() == 32, "encrypt: key length is invalid"); // ... }
|
话虽如此,在 Rust 中处理错误非常符合人体工程学,所以我认为没有充分的理由去故意panic.
可恢复的错误
应处理的错误将与Result 枚举一起返回。
pub enum Result<T, E> { Ok(T), Err(E), }
|
例如:
// Here, our error type is <code>String</code> fn ultimate_answer(guess: i64) -> Result<(), String> { if guess == 42 { return Ok(()); } return Err("Wrong answer".to_string()); }
|
现在,将 aString作为错误返回并不是很有用。事实上,同一个函数可能会返回许多不同的错误,因此越来越难以精确处理它们:
fn ultimate_answer(guess: i64) -> Result<(), String> { if guess == 42 { return Ok(()); } else if guess > 39 && guess <= 41 { return Err("A little bit more".to_string()); } else if guess <= 45 && guess > 42 { return Err("A little bit less".to_string()); } return Err("Wrong answer".to_string()); }
|
或者,许多不同的函数可以返回相同的错误:
fn do_something() -> Result<(), String> { // ... return Err("Something went wrong".to_string()); }
fn do_something_else() -> Result<(), String> { // ... return Err("Something went wrong".to_string()); }
fn do_another_thing() -> Result<(), String> { // ... return Err("Something went wrong".to_string()); }
|
这是我们需要定义自己的Error枚举的地方。通常,我们Error通过 crate 定义 1 个枚举。
pub enum Error { WrongAnswer, More, Less, }
fn ultimate_answer(guess: i64) -> Result<(), Error> { if guess == 42 { return Ok(()); } else if guess > 39 && guess <= 41 { return Err(Error::More); } else if guess <= 45 && guess > 42 { return Err(Error::Less); } return Err(Error::WrongAnswer); }
|
然后,我们可能希望为每个错误案例标准化错误消息。为此,社区选择了thiserror crate。
#[derive(thiserror::Error)] pub enum Error { #[error("Wrong answer")] WrongAnswer, #[error("A little bit more")] More, #[error("A little bit less")] Less, }
|
多亏了thiserror::Error,您的Error枚举现在实现了std::error::Error特征,因此也实现了Debug和Display特征。
然后我们可以处理一个潜在的错误match。
fn question() -> Result<(), Error> { let x = // ... match ultimate_answer(x) { Ok(_) => // do something Err(Error::More) => // do something Err(Error::Less) => // do something Err(Error::WrongAnswer) => // do something } // ... }
|
或者,处理错误的最常见方法是使用?.
fn question() -> Result<(), Error> { let x = // ... ultimate_answer(x)?; // if <code>ultimate_answer</code> returns an error, <code>question</code> stops here and returns the error. // ... }
|
这是一个快捷方式:
fn question() -> Result<(), Error> { let x = // ... match ultimate_answer(x) { Ok(_) => {}, Err(err) => return Err(err.into()), }; // ... }
|
错误转换
您的程序或库可能会使用许多依赖项,每个依赖项都有自己的错误类型,但为了能够使用?,您的Error类型需要为依赖项的错误类型实现From特征。
#[derive(Error, Debug, Clone)] pub enum Error { #[error("Internal error.")] Internal(String), #[error("Not found.")] NotFound, #[error("Permission Denied.")] PermissionDenied, #[error("Invalid argument: {0}")] InvalidArgument(String), }
impl std::convert::From<std::num::ParseIntError> for Error { fn from(err: std::num::ParseIntError) -> Self { Error::InvalidArgument(err.to_string()) } }
impl std::convert::From<sqlx::Error> for Error { fn from(err: sqlx::Error) -> Self { match err { sqlx::Error::RowNotFound => Error::NotFound, _ => Error::Internal(err.to_string()), } } }
|
最后,您可以使用.unwrap()和.expect()对可恢复的错误感到恐慌
fn do_something() -> Result<(), Error> { // ... }
fn main() { // panic if do_something returns Err(_) do_something().unwrap(); }
// or
fn main() { // panic if do_something returns Err(_) with the message below do_something().expect("do_something returned an error"); }
|