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
类型。