当前位置: 首页 > news >正文

Rust错误处理详解

错误处理是应用程序应对异常的能力。应用程序可以采取主动或被动的方式进行错误处理。

  • 主动错误处理 Rust提供了Result和Option这两个标准类型
  • 被动错误处理 Rust提供了panic。当执行过程中(运行时)发生了无法继续运行的异常事件时,就会引发panic。

Rust 彻底放弃了“异常”这种隐式传播的思路,而是走编译期强制处理错误的路线

  • 可恢复错误(Recoverable Errors):
    用 Result<T, E> 类型表示。调用者必须显式处理(match、? 运算符)。
    -> 这就是所谓的主动错误处理,错误传播路径在类型系统里清清楚楚。

  • 不可恢复错误(Unrecoverable Errors):
    用 panic! 表示。通常意味着程序逻辑出现严重问题,无法也不应该继续执行。
    -> 这就是 Rust 的被动错误处理,类似“终止程序”的紧急刹车。

panic 和异常的区别
虽然 panic 和异常都基于栈展开(stack unwinding),但理念不同:

  • 异常:设计成可以恢复的一般错误机制。
  • panic:设计成“程序出 bug 或进入不可能状态时”的终止信号。
fn divide_two_numbersing_ratio(first: i32, second: i32)-> i32 {first/second
}fn logic() {divide_two_numbersing_ratio(1, 0);
}
fn main() {logic();
}

当panic发生时,栈会按顺序展开: divide_two_numbersing_ratio和logic以及最后的main函数。
在程序终止时会输出用于诊断的错误信息,其中包含了panic发生的具体位置

thread 'main' panicked at src/bin/13_error.rs:2:4:
attempt to divide by zero

错误信息中还包含了调用栈回溯信息。

stack backtrace:0: __rustc::rust_begin_unwindat /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs:697:51: core::panicking::panic_fmtat /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/panicking.rs:75:142: core::panicking::panic_const::panic_const_div_by_zeroat /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/panicking.rs:175:173: _13_error::divide_two_numbersing_ratioat ./src/bin/13_error.rs:2:44: _13_error::logicat ./src/bin/13_error.rs:6:55: _13_error::mainat ./src/bin/13_error.rs:9:56: core::ops::function::FnOnce::call_onceat /home/cangli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:253:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

最后提示你可以运行 RUST_BACKTRACE=full cargo run,获取更详细的调用堆栈信息。

当发生panic时,栈展开的过程为应用程序提供了有序退出时机,这时最重要的是释放占用的资源和内存。

  • 有些清理工作是自动完成的。比如删除局部变量
  • 有些需要特殊处理。比如堆内存对外部资源的引用。

在栈展开的过程中,实现了Drop trait的值会自动调用drop函数。相当于其他语言的析构函数。

struct Person {name: String,age: i32,
}impl Drop for Person {fn drop(&mut self) {println!("drop被调用");}
}fn division(first: i32, second: i32) {let p = Person{name: "李四".to_string(), age: 18};first / second;
}fn logic() {let p = Person{name: "张三".to_string(), age: 18};division(1, 0);
}
fn main() {logic();
}

如果你没有想好为panic事件准备好清理策略,这种情况下,栈展开的过程可能会失去作用。更重要的是,如果没有合适的清理措施,应用程序可能会陷入未知或不稳定的状态,这种情况下最佳方案就是在panic发生时直接终止应用程序。
在Cargo.toml中添加配置来实现这一行为

[profile.dev]
panic = "abort"

需要注意的是,任何人都可以改变这个外部配置,也就意味着这种行为可能会在你的控制之外发生。除了栈回溯外,这也是panic不可预测的并且应该避免的另一种原因。

panic!宏

panic是由异常情况引发的。其实也可以使用panic!宏来主动强制引发panic.
如果可以的话还是要尽量避免panic。panic!的场景

  • 传播现有的panic
  • 无法找到可行的解决方案
  • 向应用程序发出无法拒绝的通知
fn main() {panic!("hello world")
}

包含了自定义描述信息

thread 'main' panicked at src/bin/13_error.rs:2:5:
hello world

Rust还提供一种高级版本的panic!宏,支持格式化字符串。

fn main() {panic!("hello {}", "张三")
}

处理panic

你可以根据实际情况采取不同措施。
避免栈展开进入外部代码,这可能导致无法预知的行为。比如展开到系统调用就可能引发各种问题。
在Rust中最好做必要的错误处理,如果实在无法进行错误处理,那么可以对panic进行有限的处理,比如记录panic信息并重新触发panic

  • catch_unwind方法用于处理panic,在std::panic模块中,
fn catch_unwind<F: FnOnce()->R+UnwindSafe,R>(f: F)->Result<R>

catch_unwind接受一个闭包作为参数,并返回一个Result

  • 如果闭包没有触发panic,则返回Ok(value) (其中value是闭包调用的结果)
  • 当发生panic时,该函数返回Err(error),其中error是panic的错误值
use std::any::Any;
use std::panic::catch_unwind;fn get_data(request: usize)->Result<i8, Box<dyn Any+Send>> {let vec:Vec<i8> = (0..100).collect();let ret = catch_unwind(||vec[request]);ret}
fn main() {match get_data(100) {Ok(value) => println!("{}",value),Err(_) => println!("数组访问异常")}}

可以看到,即使进行了处理,panic消息依然被输出。
其实每个线程都有一个panic hook,它是一个在panic发生时会被调用并输出panic的函数。正是这个hook函数将上面的回溯信息输出出来,当这个功能被启用的情况下,使用std::panic模块中的set_hook函数来替换这个hook,panic hook的调用在panic发生和处理之间。
解决办法是使用一个空的闭包替换默认的hook来去除panic信息

use std::any::Any;
use std::panic;fn get_data(request: usize)->Result<i8, Box<dyn Any+Send>> {let vec:Vec<i8> = (0..100).collect();panic::set_hook(Box::new(|_info|{}));let ret = panic::catch_unwind(||vec[request]);ret}
fn main() {match get_data(100) {Ok(value) => println!("{}",value),Err(_) => println!("数组访问异常")}}

要能够处理panic首先需要了解panic。与panic相关的信息会以任何类型提供,需要先将其转换为特定类型,然后才能访问有关panic的具体信息。
在前面代码中,我们忽略了panic的具体信息,现在我们需要输出panic的信息。将其向下转型为String

use std::any::Any;
use std::panic;fn get_data(request: usize)->Result<i8, Box<dyn Any+Send>> {let vec:Vec<i8> = (0..100).collect();panic::set_hook(Box::new(|_info|{}));let ret = panic::catch_unwind(||vec[request]);ret}
fn main() {match get_data(100) {Ok(value) => println!("{}",value),Err(error) => println!("{:?}",error.downcast::<String>()),}}

unwrap

在应用程序开发和测试阶段,许多开发者会使用unwrap函数来简化错误处理。
通过unwrap可以把Result或Option中的错误结果转换为panic,这种做法有两个原因。

  • 在开发阶段,展示没想好如何处理这些特定的错误。
  • 想确保错误不会被忽略。
    不过unwrap函数有一些变体适用于更多的场景,而不仅仅是开发阶段。

首先介绍unwrap函数

如果Option/Result的值为None/Err(E),就会引发panic

fn main() {let vec = [1,2,3,5,5];let ret = vec.get(5).unwrap();println!("{}",ret);
}

expect

可以用expect替换unwrap函数,区别是expect可以在panic时指定错误信息,而unwrap只有默认信息。

fn main() {let vec = [1,2,3,5,5];let ret = vec.get(5).expect("索引越界");println!("{}",ret);
}

有些unwrap函数的变体出现错误时不会引发panic,这样的方法对于错误处理非常有用。甚至可以处于非开发阶段。

unwrap_or

当出现错误时,这个方法会返回一个预设的替代值而不是直接引发panic。

fn main() {let vec = [1,2,3,5,5];let ret = vec.get(5).unwrap_or(&100);println!("{}",ret);
}

另外一个变体是unwrap_or_else

unwrap_or_else

替代值是一个闭包,这个替代值需要通过计算或涉及复杂逻辑时很有用。当unwrap出现错误(None/Err)时,会自动调用这个闭包。

fn main() {let vec = [1,2,3,5,5];let ret = vec.get(5).unwrap_or_else(|| &100);println!("{}",ret);
}

unwrap_or_default

在遇到错误时会返回对应类型的默认值。返回的具体默认值由类型决定,比如整型的默认值是0。需要注意的是,并非所有类型都有默认值,只要实现了Default trait的类型才有相应的默认值,而引用没有实现这个trait,所以不能使用这个方法

fn main() {let vec: Option<i8> = None;let ret = vec.unwrap_or_default();println!("{}",ret);
}
http://www.dtcms.com/a/411605.html

相关文章:

  • mdBook 文档
  • 女性时尚网站源码网站维护和制作怎么做会计分录
  • 怎么创建网站免费的wordpress xampp 教程
  • 宁波全网营销型网站建设哪家做网站的好
  • springboot项目整合p6spy框架,实现日志打印SQL明细(包括SQL语句和参数)
  • 【生成式模型】VAE变分自编码器分析
  • 湖北企业模板建站信息四川省住房和城乡建设厅官网证件查询
  • 做产品网站营销推广做我姓什么的网站
  • 公司如何建立微网站六盘水网站设计
  • 大模型--自编码器学习 (上)
  • 青铜峡网站建设推广重庆房地产信息官网
  • 一文读懂:大模型RAG(检索增强生成)
  • 怎么建设一个宣传网站梁山网站开发
  • Docker的介绍
  • 塘沽手机网站建设linux下搭建wordpress
  • 两篇BEVfusion原理总结及区别
  • 微信网站欣赏网站建设维护百家号
  • 发现一个可以免费在线将m3u8转换为mp4的工具
  • Linux常用命令54——ldd
  • Go tool pprof 与 Gin 框架性能分析完整指南
  • 网站开发目前主要用什么技术做宣传图片的网站
  • 住宅小区物业管理系统网站建设做网站维护有危险吗
  • 使用git pull origin master报错,fatal: refusing to merge unrelated histories
  • 易点科技网站建设档案网站建设与档案信息化
  • 昆明网站建设公司电话注册公司成本多少钱
  • OBS直播教程:听云整蛊助手如何下载?如何安装?如何使用?
  • 烟台手机网站建设电话如何免费建一个学校网站
  • 计网-VLAN
  • 做周边的网站金华网站建设公司哪个好
  • Booth算法