Rust使用kind进行异常处理(错误的分类与传递|)

   2023-02-09 学习力0
核心提示:目录前言1.1、panic! 宏的使用1.2、通过 Powershell命令行分析错误原因2、可恢复的错误2.1、RustltT,E枚举类的使用2.2、Result 类的unwrap() 和 expect(message: str) 方法3、可恢复的错误的传递4、结合kind方法处理异常前言Rust 有一套独特的处理异常情况的

前言

Rust 有一套独特的处理异常情况的机制,它并不像其它语言中的 try 机制那样简单。
在Rust 中的错误分为两大类:可恢复错误和不可恢复错误。大多数编程语言用 Exception (异常)类来表示错误。在 Rust 中没有 Exception。对于可恢复错误用 Result<T, E> 类来处理,对于不可恢复错误使用 panic! 宏来处理。

1、不可恢复错误

  • 由编程中无法解决的逻辑错误导致的,例如访问数组末尾以外的位置

1.1、panic! 宏的使用

的使用较为简单,让我们来看一个具体例子:

fn main() {
    panic!("Error occured");
    println!("Hello, rust");
}

运行结果:

Rust使用kind进行异常处理(错误的分类与传递|)

很显然,程序并不能如约运行到 println!("Hello, rust") ,而是在 panic! 宏被调用时停止了运行,不可恢复的错误一定会导致程序受到致命的打击而终止运行。

1.2、通过 Powershell命令行分析错误原因

我们来分析一下终端命令行中的报错信息:

thread 'main' panicked at 'Error occured', src\main.rs:2:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

  • 第一行输出了 panic! 宏调用的位置以及其输出的错误信息
  • 第二行是一句提示,翻译成中文就是"通过 RUST_BACKTRACE=full 环境变量运行以显示回溯"。

接下来看一下回溯(backtrace)信息:

stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:584
   1: core::panicking::panic_fmt
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:142
   2: error_deal::main
             at .\src\main.rs:2
   3: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\core\src\ops\function.rs:248

回溯是不可恢复错误的另一种处理方式,它会展开运行的栈并输出所有的信息,然后程序依然会退出。通过大量的输出信息,我们可以找到我们编写的 panic! 宏触发的错误。

2、可恢复的错误

  • 如果访问一个文件失败,有可能是因为它正在被占用,是正常的,我们可以通过等待来解决。

2.1、Rustlt<T,E>枚举类的使用

此概念十分类似于 Java 编程语言中的异常,而在 C 语言中我们就常常将函数返回值设置成整数来表达函数遇到的错误,在 Rust 中通过 Result<T, E> 枚举类作返回值来进行异常表达:

enum Result<T, E> {
    Ok(T),
    Err(E),
}//T的类型不定,相当于C++中模板的写法

我们知道enum常常与match配合使用,当匹配到OK时就会执行相应代码。

在 Rust 标准库中可能产生异常的函数的返回值都是 Result 类型。

例如:当我们尝试打开一个文件时:

use std::fs::File;

fn main() {
    let fp = File::open("hello_rust.txt");
    match fp {
        Ok(file) => {
            println!("File opened successfully.");
        },
        Err(err) => {
            println!("Failed to open the file.");
        }
    }
}//OK里的参数file是File类型,相当于填充了枚举里的T类型

如果 hello_rust.txt 文件不存在,会打印 Failed to open the file.

当然,我们在枚举类章节讲到的 if let 模式匹配语法可以简化 match 语法块:

use std::fs::File;

fn main() {
    let fp = File::open("hello_rust.txt");
    if let Ok(file) = fp {
        println!("File opened successfully.");
    } else {
        println!("Failed to open the file.");
    }
}

2.2、Result 类的unwrap() 和 expect(message: &str) 方法

将一个可恢复错误按不可恢复错误处理

举个例子:

use std::fs::File;

fn main() {
    let fp1 = File::open("hello_rust.txt").unwrap();
    let fp2 = File::open("hello_rust.txt").expect("Failed to open.");
}
  • 这段程序相当于在 Result 为 Err 时调用 panic!宏
  • 两者的区别在于 expect 能够向 panic! 宏发送一段指定的错误信息
  • panic!宏是不可恢复错误,这样就完成了转变

3、可恢复的错误的传递

之前所讲的是接收到错误的处理方式,接下来讲讲怎么把错误信息传递出去

我们先来编写一个函数:

fn f(i: i32) -> Result<i32, bool> {
    if i >= 0 {
         Ok(i) 
        }
    else { 
        Err(false) 
    }
}
fn main() {
    let r = f(10000);
    if let Ok(v) = r {
        println!("Ok: f(-1) = {}", v);
    } else {
        println!("Err");
    }
}//运行结果:Ok: f(-1) = 10000

这里r的结果是f函数返回的ok(10000),经过if let模式匹配后v的值为10000

这段程序中函数 f 是错误的根源,现在我们再写一个传递错误的函数 g

fn g(i: i32) -> Result<i32, bool> {
    let t = f(i);
    return match t {
        Ok(i) => Ok(i),
        Err(b) => Err(b)
    };
}

函数 g 传递了函数 f 可能出现的错误,这样写有些冗长,Rust 中可以在 Result 对象后添加 ? 操作符将同类的 Err 直接传递出去:

fn f(i: i32) -> Result<i32, bool> {
    if i >= 0 { Ok(i) }
    else { Err(false) }
}

fn g(i: i32) -> Result<i32, bool> {
    let t = f(i)?;
    Ok(t) // 因为确定 t 不是 Err, t 在这里已经推导出是 i32 类型
}

fn main() {
    let r = g(10000);
    if let Ok(v) = r {
        println!("Ok: g(10000) = {}", v);
    } else {
        println!("Err");
    }
}//运行结果:Ok: g(10000) = 10000

? 符的实际作用是将 Result 类非异常的值直接取出,如果有异常就将异常 Result 返回出去。所以? 符仅用于返回值类型为 Result<T, E> 的函数,且其中 E 类型必须和 ? 所处理的 Result 的 E 类型一致。

4、结合kind方法处理异常

虽然前面提到Rust 异常不像其他语言这么简单,但这并不意味着 Rust 实现不了:我们完全可以把 try 块在独立的函数中实现,将所有的异常都传递出去解决。

实际上这才是一个分化良好的程序应当遵循的编程方法:应该注重独立功能的完整性。

但是这样需要判断 Result 的 Err 类型,获取 Err 类型的函数是 kind()

做一个打开文件的实例:

use std::io;
use std::io::Read;
use std::fs::File;

fn read_text_from_file(path: &str) -> Result<String, io::Error> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

fn main() {
    let str_file = read_text_from_file("hello_rust.txt");
    match str_file {
        Ok(s) => println!("{}", s),
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!("No such file");
                },
                _ => {
                    println!("Cannot read the file");
                }
            }
        }
    }
}//这里我没有创建hello_rust.txt文件,因此运行结果为:No such file

代码解释:

  • 使用read_text_from_file()函数将文件打开的结果传给了str_file变量
  • 这里并不存在hello_rust.txt,因此File::open(path)?不会打开文件,异常会存到f
  • f.read_to_string(&mut s)?并不能读出文件内容,ok(s)无内容
  • 通过分析,分支会执行Err(e)的代码块,使用e.kind()得到了错误类型并再次进行match分支
  • 如果是NotFound错误就会打印No such file
  • 其他情错误均提示Cannot read the file
原文地址:https://blog.csdn.net/m0_58618795/article/details/127034383
 
标签: Rust 错误 异常
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • bloom-server 基于 rust 编写的 rest api cache 中间件
    bloom-server 基于 rust 编写的 rest api cache
    bloom-server 基于 rust 编写的 rest api cache 中间件,他位于lb 与api worker 之间,使用redis 作为缓存内容存储, 我们需要做的就是配置proxy,同时他使用基于share 的概念,进行cache 的分布存储,包含了请求端口(proxy,访问数据) 以及cache 控制端口(
    03-08
  • #新闻拍一拍# Oracle 调研如何避免让 Java 开发者投奔 Rust 和 Kotlin | Linux 中国
    #新闻拍一拍# Oracle 调研如何避免让 Java 开发
     导读:• 英特尔对迟迟不被 Linux 主线接受的 SGX Enclave 进行了第 38 次修订 • ARM 支持开源的 Panfrost Gallium3D 驱动本文字数:977,阅读时长大约:1分钟作者:硬核老王Oracle 调研如何避免让 Java 开发者投奔 Rust 和 KotlinOracle 委托分析公司 Omd
    03-08
  • Linux系统下Rust快速安装:国内镜像加速
    Linux系统下Rust快速安装:国内镜像加速
    官方网址和方法Install Rust - Rust Programming Language然而速度慢得让人难以置信。利用国内镜像进行windows的Linux子系统的Rust安装。rust 使用国内镜像,快速安装方法参考:RUST安装慢怎么办,使用镜像方式安装_网络_为中华之崛起而编程-CSDN博客我的操作
    03-08
  • Rust到底值不值得学--Rust对比、特色和理念
    前言其实我一直弄不明白一点,那就是计算机技术的发展,是让这个世界变得简单了,还是变得更复杂了。当然这只是一个玩笑,可别把这个问题当真。然而对于IT从业者来说,这可不是一个玩笑。几乎每一次的技术发展,都让这个生态变得更为复杂。“英年早秃”已经成
    03-08
  • 超33000行新代码,为Linux内核添加Rust支持的补丁已准备就绪
    超33000行新代码,为Linux内核添加Rust支持的补
    https://mp.weixin.qq.com/s/oKw9aBJSdmRoO6-rbLAkNw7 月 4 日,一套修订后的补丁被提交至 Linux 内核的邮件列表中,该补丁为在 Linux 内核中以 Rust 作为辅助编程语言提供了支持,借助 Rust 可以提高 Linux 内核和内存的安全。整套补丁包含 17 个子项,不光
    03-08
  • 【译】Rust 的 Result 类型入门
    【译】Rust 的 Result 类型入门
    A Primer on Rust’s Result Type 译文原文链接:https://medium.com/@JoeKreydt/a-primer-on-rusts-result-type-66363cf18e6a原文作者:Joe Kreydt译文出处:https://github.com/suhanyujie/article-transfer-rs译者:suhanyujietips:水平有限,翻译不当之
    03-08
  • Rust实战系列-基本语法
    Rust实战系列-基本语法
    主要介绍 Rust 的语法、基本类型和数据结构,通过实现一个简单版 grep 命令行工具,来理解 Rust 独有的特性。本文是《Rust in action》学习总结系列的第二部分,更多内容请看已发布文章:一、Rust实战系列-Rust介绍“主要介绍 Rust 的语法、基本类型和数据结
    03-08
  • 全栈程序员的新玩具Rust(三)板条箱
    上次用到了stdout,这次我们来写一个更复杂一点的游戏rust的标准库叫做std,默认就会引入。这次我们要用到一个随机数函数,而随机数比较尴尬的一点是这玩意不在标准库中,我们要额外依赖一个库。很多编程方案都有自己的模块化库系统,rust也不例外,不过rust
    02-10
  • 全栈程序员的新玩具Rust(六)第一个WASM程序
    全栈程序员的新玩具Rust(六)第一个WASM程序
    先上代码https://gitee.com/lightsever/rust_study/tree/master/wasm_hello01webassembly就不用再赘述了,耳朵里面快磨出茧子来了。rustwasm是火狐自家的玩具,让我们来继续做实验,让rust飞起来吧。环境安装安装好rust环境之后仍然需要 一个 wasm 工具包carg
    02-10
  • 【Rust】标准库-Result rust数据库
    环境Rust 1.56.1VSCode 1.61.2概念参考:https://doc.rust-lang.org/stable/rust-by-example/std/result.html示例main.rsmod checked {#[derive(Debug)]pub enum MathError {DivisionByZero,NonPositiveLogarithm,NegativeSquareRoot,}pub type MathResult =
    02-09
点击排行