Panic
)Result
Result
处理。 None
的 Option
上调用.unwrap()
Panic!()
,用于在代码中主动触发诧异。接收可选的 println!()
风格的参数,用于构建错误消息。RuntimeException
或 C++ 语言中的 std::logic_error
。std::panic::catch_unwind
,可以捕获栈展开,允许诧异线程存活并继续进行。 catch_unwind()
来处理诧异。在以下两种情况,程序发生 Bug 后会中止进程:
在 Rust 展开第一个函数后的清理期间,
.drop()
方法触发了第二个诧异:
Rust 的诧异行为是自定义的情况:
-C panic=abort
,那么编译后程序中的第一个诧异就会立即中止进程。Rust 没有异常,但是相反,当函数执行失败时,可以通过一个返回的 Result
类型来表示。
fn get_weather(location: LatLng) -> Result<WeatherReport, io::Error>
Ok(weather)
,此时 weather
的值是一个新的 WeatherReport
值。Err(error_value)
,此时 error_value
就是一个解释除了什么错误的 io::Error
值。处理 Result
的一种方式,是使用 match
表达式。
// 相当于Python中的try机制
match get_weather(hometown) {ok(report) => {display_weather(hometown, &report);}Err(err) => {println!("error querying the weather: {}", err);schedule_weather_retry();}
}
处理 Result
的另一种方式,是使用 Result<T, E>
提供的几种方法,每种方法都有一个形如上述的 match
表达式。如下所示常用的方法有:
result.is_ok()
和 result.is_err()
返回 bool
值:告知 result
是成功的结果还是错误的结果。
result.ok()
返回 Option<T>
类型的成功值:成功返回 Some(success_value)
;失败返回 None
,并丢弃错误值。
<() 返回 Option<E>
类型的错误值。
result.unwrap_or(fallback)
返回 T
类型的成功值。如果失败返回一个后备值 fallback
,并丢弃错误值。
// 设定一个后备值,用来预测某地天气
const THE_UAL: WeatherReport = WeatherReport::Sunny(72);// 如果预测成功,返回实时的天气情况
// 如果预测失败,则以常规的值为后备
let report = get_weather(los_angeles).unwrap_or(THE_UAL);
display_weather(los_angeles, &report);
result.unwrap_or_else(fallback_fn)
返回 T
类型的成功值。但是传入的参数是一个函数或闭包。只有在返回错误结果时,才会调用 fallback_fn
。
let report = get_weather(hometown).unwrap_or_else(|_err| vague_prediction(hometown));
result.unwrap()
会返回 T
类型的成功值。如果失败,会导致诧异。
result.unwrap()
相同,不过需要提供诧异时打印到控制台的消息 message
。
借用 Result
中值的引用的方法,Result
的原数据结果不会损坏:
result.as_ref()
将 Result<T, E>
转换为 Result<&T, &E>
,即借用现有 result
中成功或错误的引用。result.as_mut()
,借用的是可修改引用。返回的类型为 Result<&mut T, &mut E>
。Result
类型别名省略 Result
的错误类型,即为 Result
类型别名:
fn remove_file(path: &Path) -> Reust<() >
pub type Result<T> = result::Result<T, Error>;
std::io::Result<T>
,以硬编码的 std::io::Error
作为错误类型的 Result<T, E>
的别名。实际使用中,如果 use std::io
,那么 Rust 就会认为 io::Reust<String>
是 Result<String, io::Error>
的简写将错误转存到终端,然后继续执行:
println!("error querying the weather: {}", err);
std:io::Error
、std::fmt::Error
、std::str::Utf8Error
等错误类型有相同的公共接口:std::error::Error
特型,它们都支持以下特点:
println!()
来打印。打印错误时以 {}
作为格式描述符,可以显示简略的错误消息;也可以使用 {:?}
为格式描述符,这样可以打印 debug 版的错误消息。err.description()
方法,返回 &str
类型的错误消息。err.cause()
方法,返回一个 Option<&Error>
类型,这是触发 err
的底层错误。即错误的真正原因。打印错误消息不一定会打印出错误的原因。如果需要打印所有可用信息,可使用如下函数:
use std::error::Error;
use std::io::{Write, stderr};/// 把错误消息转存到stderr
///
/// 如果在构建当前错误消息,或写入stderr时,发生了另一个错误,则忽略该错误。
fn print_error(mut err: &Error) {let _ = writeln!(stderr(), "error: {}", err);while let Some(cause) = err.cause() {let _ = writeln!(stderr(), "caused by: {}", cause);err = cause;}
}
标准库的错误类型不包含栈追踪信息,但是使用 error-chain
包可以方便地定义自己的错误类型,以支持在创建时获取栈追踪信息。这个包使用了 backtrace
捕获栈信息。
?
操作符try!()
宏?
操作符:可以在任何产生 Result
的表达式后面添加 ?
,如:let weather = get_weather(hometown)?;
如果返回了成功结果,那么 ?
操作符会打开 Result
并取出其中的成功值。
如果返回了错误结果,那么 ?
操作符会立即从闭合函数中返回,将错误结果沿调用链向上传播。
只能对返回值为 Result
的函数使用 ?
。
上述代码等价于:
let weather = match get_weather(hometown) {ok(success_value) => success_value;Err(err) => return Err(err)
};
try!()
宏:扩展为一个类似上述 match
的表达式。
let weather = try!(get_weather(hometown));
?
操作符的用法举例:
use std::fs;
use std::io;
use std::path::Path;fn move_all(src: &Path, dst: &Path) -> io::Result<()> {for entry_result ad_dir()? { // 打开dir可能失败let entry = entry_result?; // 读取dir可能失败let dst_file = dst.join(entry.file_name());fs::rename(entry.path(), dst_file)?; // 重命名可能失败}ok(()) // 如果执行成功,则返回成功值
}
从文件中读取一行内容,并解析为整数时,会产生两种不同的潜在错误类型:
use std::io::{self, BufRead};/// 从文本文件中读取整数
/// 这个文件中的每一行应该都有一个数值
fn read_numbers(file: &mut BufRead) -> Result<Vec<i64>, io::Error> {let mut numbers = vec![];for line_result in file.lines() {let line = line_result?; // 读取行的内容可能失败numbers.push(line.parse?); // 解析整数有可能失败}Ok(numbers)
}
line_result
的错误类型是 Result<String, std::io::Error>
;
line.parse()
的错误类型是 Result<i64, std::num::ParseIntError>
;在编译时,Rust 会常识把 ParseIntError
转换为 io::Error
,但是这种转换不存在,就会得到如下的类型错误:
the trait 'std::convert::From<std::num::ParseIntError>' is not implemented for 'std::io::Error'
处理上述错误的方法:
采用 error-chain
包;
采用内置特性:所有标准库的错误类型都可以转换为 Box<std::error::Error>
类型,因此可以定义如下类型别名:
type GenError = Box<std::error::Error>;
type GenResult<T> = Result<T, GenError>;
// 然后,可以把read_numbers()的返回类型改为GenResult<Veci64>
// ?操作符,会根据需求,自动将任意错误类型转换为GenError。
把任意错误转换为 GenError
类型,也可以调用 GenError::from()
方法。
let io_error = io::Error::new( // 创建一个自定义的io::Errorio::ErrorKind::Other, "timed out"
);
return Err(GenError::from(io_error)); // 手工转换为GenError
使用 GenError
的缺点:返回类型不再精确地传达调用者可以预测的错误类型。如果调用的函数返回 GenResult
,但只想处理一种特定的错误,让其他所有错误传播出去,那么可以使用泛型方法 error.downcast_ref::<ErrorType>()
。如果恰好是想要的那个错误类型,那么该方法会借用这个错误的引用:
loop {match compile_project() {Ok(()) => return Ok(()),Err(err) => {if let Some(mse) = err.downcast_ref::<MissingSemicolonError>() {insert_semicolon_in_source_code(mse.file(), mse.line())?;continue;}return Err(err);}}
}
把数字字符串转换成实际的数值:
let num = str.parse::<u64>();let num = str.parse::<u64>().unwrap();
// 处理当字符串不是数字时的编写错误,用诧异输出
// 但是如果数字过长,而超过了u64,那么此时会产生一个bug
如果错误代表的是一个非常严格或奇怪的条件,希望在出错时差异,那么可以采用如下的.elapsed()
方法:
fn print_file_age(filename: &Path, last_modified: SystemTime) {let age = last_modified.elapsed().expect("System clock drift");...
}
// .elapsed()方法,只有当系统时间早于文件创建时间时才会失败
// 此时会出发诧异,而不是处理错误或将错误传播给调用者。
let _ = ...
:用来禁止某些错误
let _ = writeln!(stderr(), "error: {}", err);
main()
函数不能使用 ?
操作符
.expect()
方法:在 main()
函数中处理错误的简单方式
fn main() {calculate_tides().expect("error");
}
// 主线程中的诧异会打印错误消息,然后以一个非零退出码退出。
// 在较小的程序中比较推荐使用。
优化上述代码:采用 if let
表达式,只在调用 calculate_tides()
返回错误时打印错误消息。
use std::error::Error;
use std::io::{Write, stderr};/// 把错误消息转存到stderr
///
/// 如果在构建当前错误消息,或写入stderr时,发生了另一个错误,则忽略该错误。
fn print_error(mut err: &Error) {let _ = writeln!(stderr(), "error: {}", err);while let Some(cause) = err.cause() {let _ = writeln!(stderr(), "caused by: {}", cause);err = cause;}
}fn main() {if let Err(err) = calculate_tides() {print_error(&err);std::process::exit(1);}
}
use std;
use std::fmt;// 编写一个JSON解释器,并让它有自己的错误类型
// json/src/error.rs,可以通过json::error::JsonError调用
#[derive(Debug, Clone)]
pub struct JsonError {pub message: String,pub line: usize,pub column: usize,
}/// 方法一:简单创建
return Err(JsonError {message: "expected ']' at end of array".to_string(),line: current_line,column: current_column
});/// 方法二:接近标准错误类型
// 错误可以打印出来
impl fmt::Display for JsonError {fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {write!(f, "{} ({}:{})", ssage, self.line, self, lumn)}
}// 错误实现std::error::Error特型
impl std::error::Error for JsonError {fn description(&self) -> &str {&ssage}
}
详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第七章
链接地址
本文发布于:2024-01-30 22:58:45,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170662672923450.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |