Error Handling¶
本节介绍Rust的异常处理机制。
编译器错误¶
Rust中的部分错误可以在编译时被检测出来,这些错误被称为编译时错误(compile-time error)。使用rustc --explain可以查看编译时错误的详细信息。
rustc --explain E0382
# A variable was used after its contents were moved elsewhere.
#
# Erroneous code example:
#
# struct MyStruct { s: u32 }
#
# fn main() {
# let mut x = MyStruct { s: 5u32 };
# let y = x;
# x.s = 6;
# println!("{}", x.s);
# }
# ...
异常(Panic)¶
Rust中的异常被称为panic。当程序发生panic时,Rust会打印出异常信息并退出程序。有多个宏可以用于触发异常。
// 以下的宏也可以接受类似println!的格式化字符串
panic!("This is a panic!");
unimplemented!(); // 用于标记未实现的代码
unreachable!(); // 用于标记不可能到达的分支
// 以下宏用于测试
assert!(false); // 用于测试表达式是否为true
assert_eq!(1, 2); // 用于测试两个表达式的值是否相等
assert_ne!(1, 1); // 用于测试两个表达式的值是否不相等
// 在Debug模式或使用-C debug-assertions参数时,以下宏才会触发
debug_assert!(false);
debug_assert_eq!(1, 2);
debug_assert_ne!(1, 1);
异常处理¶
Rust中内置了两个类型Option<T>和Result<T, E>用于异常处理,其定义为
enum Option<T> {
None,
Some(T),
}
enum Result<T, E> {
Ok(T),
Err(E),
}
以下,均以T代表Option::Some或Result::Ok的类型,E代表Result::Err的类型。
-
Option类型用于表示一个可能不存在的值,常用于函数参数、结构体字段或函数返回值,使用match语句可以匹配Option类型取值的不同情况。fn main() { let x: Option<i32> = Some(5); match x { Some(i) => println!("{}", i), None => println!("None"), } }- 使用
is_some和is_none方法可以快速判断Option类型是否为Some或None。 - 使用
unwrap方法可以将Option类型转换为T类型,如果Option类型为None,则会触发异常。
- 使用
-
Result类型用于表示一个可能出错的值,常用于函数返回值。Ok表示函数执行成功,Err表示函数执行失败。fn main() { let x: Result<i32, &str> = Ok(5); match x { Ok(i) => println!("{}", i), Err(e) => println!("{}", e), } }- 使用
is_ok和is_err方法可以快速判断Result类型是否为Ok或Err。 -
使用
ok和err方法可以将Result<T, E>类型转换为Option<T>类型。如果调用的方法和Result类型的取值相同,则返回Some(T),否则返回None。fn main() { let x: Result<i32, &str> = Ok(5); let y: Option<i32> = x.ok(); println!("{:?}", y); // Some(5) let x: Result<i32, &str> = Err("error"); let y: Option<i32> = x.ok(); println!("{:?}", y); // None } -
使用
unwrap方法或expect方法可以将Result类型转换为T类型,如果Result类型为Err,则会触发异常,输出的异常信息对应于Err中的值或expect方法的参数。fn main() { let x: Result<i32, &str> = Ok(5); let y: i32 = x.unwrap(); println!("{}", y); // 5 let x: Result<i32, &str> = Err("error"); let y: i32 = x.unwrap(); // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "error"', src/main.rs:2:26 println!("{}", y); }
- 使用
unwrap方法有如下变种:
expect方法,接受一个字符串作为参数,用于指定跳出异常时的提示信息。- (仅
Result类型)unwrap_err方法和expect_err方法,与unwrap相反,当Result类型为Ok时,会触发异常,否则返回Err中的值。 unwrap_or方法,接受一个T类型的参数,在unwrap方法会触发异常的情况下,返回该参数。-
unwrap_or_default方法,在unwrap方法会触发异常的情况下,返回该默认值。该方法要求T类型实现了Default特性。struct MyStruct { s: i32 } fn main() { let a: Option<MyStruct> = None; println!("{}", a.unwrap_or_default().s); // error[E0277]: the trait bound `MyStruct: Default` is not satisfied } -
unwrap_or_else方法,接受一个函数作为参数,在unwrap方法会触发异常的情况下,返回函数的返回值。其中,Result类型的闭包参数为E类型,Option类型的闭包参数为()类型,函数的返回值需要和Some或Ok的类型相同。fn increment(x: i32) -> i32 { x + 1 } fn main() { let a: Result<i32, i32> = Err(2); println!("{}", a.unwrap_or_else(increment)); // 3 }
运算¶
Rust可以在不取出Option或Result类型的值的情况下,对其进行运算。对于以下方法,记A为调用方法的对象,B为方法的参数,即A.method(B)。
Option和Result的真值
不妨将Option和Result类型的Some和Ok视为真,其中的值T称为真值,None和Err视为假,Err的值E视为假值。
- 逻辑运算
A.or(B)与A.and(B)方法,接受一个和自身类型相同的参数。- 对于
or(),如果A为真,则返回A的真值,否则返回B值。 - 对于
and(),如果A为假,则返回A的假值,否则返回B。
- 对于
A.or_else()为A.or(B)的变种,接受一个函数作为参数。如果为真,则返回A的真值,否则返回函数的返回值。A.and_then()为A.and(B)的变种,接受一个函数作为参数。如果为假,则返回A的假值,否则返回函数的返回值。- (仅
Option类型)A.filter(),接受一个函数fn (T) -> bool作为参数。如果A为真,且以其真值作为参数后调用函数返回真,则返回A的真值,否则返回None。 A.map(),接受一个函数fn (T) -> U作为参数。如果A为真,则返回A的真值作为参数后调用函数的返回值,否则返回A的假值。注意返回值的类型为Option<U>或Result<U, E>。- (仅
Result类型)A.map_err(),接受一个函数fn (E) -> F作为参数。如果A为假,则返回A的假值作为参数后调用函数的返回值,否则返回A的真值。 - (仅
Option类型)A.map_or(),接受一个U类型的默认值和一个函数fn (T) -> U作为参数。如果A为真,则返回A的真值作为参数后调用函数的返回值,否则返回默认值。 A.map_or_else(),接受一个函数fn () -> U和一个函数fn (T) -> U作为参数。如果A为真,则返回A的真值作为参数后调用第二个函数的返回值,否则返回第一个函数的返回值。
- (仅
- (仅
Option类型)A.ok_or()用于将Option类型转换为Result类型,接受一个E类型的参数。如果A为真,则返回A的真值,否则返回参数作为Err的值。A.ok_or_else()为A.ok_or()的变种,接受一个函数作为参数。如果A为真,则返回A的真值,否则返回函数的返回值。
A.as_ref()和A.as_mut(),用于将Option<T>或Result<T, E>类型转换其对应的引用。其中A.as_ref()返回不可变引用,即Option<&T>或Result<&T, &E>;A.as_mut()返回可变引用,即Option<&mut T>或Result<&mut T, &mut E>。
异常传递¶
Rust中,有两种方式可以向上返回异常。
-
?运算符,用于将Option或Result类型转换为T类型,如果Result类型为Err或,则会直接返回Err中的值。fn fn_with_error<'a>() -> Result<i32, &'a str> { Err("Deterministic error") } fn use_fn<'a>() -> Result<i32, &'a str> { let x = fn_with_error()?; // Directly return Err("Deterministic error") Ok(x) // unreachable } fn main() { let x: &str = use_fn().unwrap_err(); println!("{}", x); // Deterministic error } -
try!宏,与?的效果等同,使用方式为try!(expression)。目前已经被弃用,使用?代替。fn fn_with_error<'a>() -> Result<i32, &'a str> { Err("Deterministic error") } fn use_fn<'a>() -> Result<i32, &'a str> { let x = try!(fn_with_error()); // error: use of deprecated `try` macro Ok(x) // unreachable } fn main() { let x: &str = use_fn().unwrap_err(); println!("{}", x); // Deterministic error }
主函数¶
Rust的main函数只能取()或Result<(), E>类型,其中E为std::error::Error的实现类型。如果main函数返回Result类型,则会将Err中的值打印到stderr并退出程序。
use std::fs::File;
fn main() -> std::io::Result<()> {
let _ = File::open("not-existing-file.txt")?; // Result::Err
Ok(()) // Default return value
// Must with this line, otherwise return () which is incompatible
}
如果main函数的返回值类型不是()或Result,则无法通过编译。
fn main() -> i32 {
0
}
// error[E0277]: `main` has invalid return type `i32`
自定义异常¶
在Rust中可以自定义异常类型。异常类型需要实现std::error::Error特性,该特性定义了以下方法:
fn source(&self) -> Option<&(dyn Error + 'static)>,用于返回引起异常的原因(可选)。fn Debug::fmt(&self, f: &mut Formatter<'_>) -> Result,用于格式化异常信息(用于调试)。fn Display::fmt(&self, f: &mut Formatter<'_>) -> Result,用于格式化异常信息(用于输出)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
异常转换¶
由于Rust中的异常类型是静态的,因此在异常传递过程中,可能会遇到异常类型不匹配的情况。此时,可以使用From特性将异常类型转换为另一种异常类型。
以下以std::io::Error为例。
impl std::convert::From<std::io::Error> for MyError {
fn from(error: std::io::Error) -> Self {
MyError { code: 2, message: error.to_string() }
}
}
此后,无需显式调用From特性的方法,即可将std::io::Error类型转换为MyError类型。