《Rust程序设计》学习笔记六:错误处理

===^^===

《榴芒客服系统》是我们工作室开发的在线客服系统,欢迎下载试用:

https://blog.csdn.net/look4liming/article/details/164755808https://blog.csdn.net/look4liming/article/details/164755808

Rust中的两类错误处理:panic和Result。

普通错误使用Result类型来处理。

panic针对的是另一种错误,即那种永远不应该发生的错误。

当程序出现下列问题时,会引发panic:

数组越界访问;

整数除以0;

在恰好为Err的Result上调用.expect();

断言失败。

Rust既可以在发生panic时展开调用栈,也可以终结进程。展开调用栈时默认方案。

整数除以0,会触发panic,通常会按如下方式处理:

把错误信息打印到终端。

展开调用栈。当前函数使用的任何临时值、局部变量或参数都将按照与创建它们时的相反的顺序被丢弃。丢弃一个值仅仅意味着随后会进行清理:程序正在使用的任何字符串或向量都将被释放,所有打开的文件都将被关闭,等等。还会调用有用户定义的drop方法。

最后,线程退出。如果是主线程,则整个进程退出(使用非零退出码)。

panic不是崩溃,也不是未定义行为。它更像是Java中的RuntimeException或C++中的std::logic_error。

panic是安全的,不会在内存中留下悬空指针或半初始化的值。

panic是基于线程的。一个线程panic时,其它线程可以继续做自己的事。

调用栈展开时默认的panic处理行为,但在两种情况下Rust不会试图展开调用栈。

如果Rust在试图清理第一个panic时,.drop方法触发了第二个panic,那么这个panic就是致命的。Rust会停止展开调用栈并中止整个进程。

如果使用-C panic=abort参数进行编译,那么程序中的第一个panic会立即中止进程。

Rust中没有异常,函数执行失败时会有像下面这样的返回类型:

fn get_weather(location: LatLng) -> Result<WeatherReport, io::Error>

使用match表达式处理Result:

match get_weather(hometown) {

Ok(report) => {

Display_weather(hometown, &report);

}

Err(err) => {

println!("error querying the weather: {}", err);

schedule_weather_retry();

}

}

match有点复杂,一次Result<T, E>针对一些常见的特定场景提供了多个有用的方法:

result.is_ok()和result.is_err(),这两个方法返回bool类型的值,用于表示result是已成功,还是已出错。

result.ok(),返回Option<T>。

如果result是成功的,就返回Some(success_value);否则返回None,并丢弃错误值。

result.err(),以Option<E>类型返回错误值。

result.unwrap_or(fallback),如果result是成功结果,就返回成功值;否则,返回fallback,丢弃错误值。

result.unwrap(),如果result是成功结果,此方法同样会返回成功值;如果result是错误结果,则会引发panic。

result.expect(message),与.unwrap()行为相同,但允许你提供一条消息,在发生panic时打印出该消息。

result.as_ref(),将Result<T, E>转换为Result<&T, &E)。

result.as_mut(),将Result<T, E>转换为Result<&mut T, &mut E)。

类型别名是类型名称的简写形式。标准库中的std::io模块包括下面这行代码:

pub type Result<T> = result::Result<T, Error>;

上面的语句定义了公共类型std::io::Result<T>,它是Result<T, E>的别名,但将错误类型硬编码为std::io::Error。

所有错误类型都可以通过println!()打印出来。使用格式说明符{}打印错误通常只会显示一条简短的错误消息。使用格式说明符{:?},以获得该错误的Debug试图,这对用户不太友好,但包含额外的技术信息。

打印错误信值并不会打印出其来源。如果想确保打印所有可用信息,请使用下面这个函数:

use std::error::Error;

use std:io::{Write, stderr};

/// 把错误信息转储到stderr

/// 如果在构建此错误信息或将其写入stderr期间发生了另一个错误,就忽略新错误

fn print_error(mut err: &dyn Error) {

let _ = writeln!(stderr(), "error: {}", err);

while let Some(source) = err.source() {

let _ = writeln!(stderr(), "caused by: {}", source);

err = source;

}

}

writeln!宏会将数据写入所选的流。在这里,将错误信息写入了标准错误流std::io::stderr。

大多数时候,可能不想立即捕获并处理错误。当发生某种错误时,我们通常希望让调用者去处理。希望错误沿着调用栈向上传播。

?运算符可以实现此操作:

let weather = get_weather(hometown)?;

?的行为取决于此函数是返回了成功结果还是错误结果。

如果是成功结果,那么它会解包Result以获取其中的成功值。

如果是错误结果,那么它会立即从所在函数返回,将错误结果沿着调用链向上传播。?只能在返回类型为Result的函数中的Result值上使用。

?运算符并无任何神奇之处。可以使用match表达式来表达同样的意图:

let weather = match get_weather(hometown) {

Ok(success_value) => success_value,

Err(err) => return Err(err)

}

try!宏,在引入?运算符之前,是船舶错误的常用方法:

let weather = try!(get_weather(hometown));

try!宏会扩展为一个match表达式。

?运算符也能用于Option类型。在返回Option类型的函数中,也可以使用?解包某个值,当遇到None时就会提前返回:

let weather = get_weather(hometown).ok()?;

所有标准库中的错误类型都可以转换为类型:

Box<dyn std::error::Error + Send + Sync + 'static>

为便于使用,还可以定义类型别名:

type GenericError

= Box<dyn std::error::Error + Send + Sync + 'static>;

Type GenericResult<T> = Result<T, GenericError>;

然后,将read_numbers()的返回类型改为GenericResult<Vec<i64>>。这样一来,函数就可以编译了。?运算符会根据需要自动将任意类型的错误转换为GenericError。

GenericError方法的缺点是返回类型不再准确地传达调用者可预期的错误类型。调用者必须做好应对任何情况的准备。

如果你正在调用一个返回GenericResult的函数,并且想要处理一种特定类型的错误,而让所有其它错误传播出去,那么可以使用泛型方法:

error.downcast_ref::<ErrorType>()

main函数不能使用?,因为它的返回类型不是Result:

fn main() {

calculate_tides()?; // 错误,main()无法再推卸责任了

}

处理main()函数中错误的简单方式是使用.expect():

fn main() {

calculate_tides().expect("error"); // 责任止于此

}

如果calculate_tides()返回错误,那么.expect()会panic。主线程中的panic会打印出一条错误消息,然后以非零的退出码退出。

也可以更改main()的类型签名以返回Result类型,这样就可以使用?了:

fn main() -> Result<(), TideCalcError> {

let tides = calculate_tides()?;

print_tides(tides);

Ok(())

}

===^^===

《榴芒客服系统》是我们工作室开发的在线客服系统,欢迎下载试用:

https://blog.csdn.net/look4liming/article/details/164755808https://blog.csdn.net/look4liming/article/details/164755808

相关推荐
达子6664 小时前
RUST 图解 第 3 章:通用编程概念(Common Programming Concepts)
开发语言·算法·rust
对象存储与RustFS13 小时前
HelixDB 上手:Agent 记忆新方案,基于 RustFS 对象存储,附完整 docker-compose 部署示例
后端·rust·开源
Amos_Web13 小时前
Rspack 源码解析(二十):Typed Hook 系统如何靠宏生成
前端·rust·前端框架
Source.Liu14 小时前
【A11】拆分前端,实现多页面方案
前端·rust
Kapaseker15 小时前
别只会 unwrap():Rust Result 的几种实用写法
rust
DongQiShanRen1 天前
玄龙(上):TICK 主循环——意识心跳怎么跳
linux·jvm·数据库·人工智能·数据挖掘·rust
编程序的员1 天前
使用OpenTelemetry来监控Rust应用
运维·rust·monitoring·opentelemetry
对象存储与RustFS2 天前
JuiceFS + 对象存储:把 S3 变成 POSIX 文件系统实测
后端·rust·开源
遇码2 天前
侧边栏一开,AI 画的东西就被挡住了:给白板画布加「真实可见区」计算和相机补偿
人工智能·ai·rust