
Panic)ResultResult处理。 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 条评论) |