首页 > 其他分享 >Rust 错误处理

Rust 错误处理

时间:2024-05-09 14:24:05浏览次数:15  
标签:mut MyError String file msg path 错误处理 Rust

rust 处理错误,不使用 try catch, 而是使用 Result<T, E>。

简单的处理rust错误

在各种关于rust错误处理的文档中,为了解释清楚其背后的机制,看着内容很多,不好理解。

比如我们写一个方法,读取文件内容:

fn read_file_to_string(file_path: String) -> Result<String, io::Error>{
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

上面的代码,当文件不存在的时候,也可以很好的返回异常信息。

调用代码:

    let r = read_file_to_string(r"d:\1111.txt".to_string());
    match r {
        Ok(str) => println!("OK: {str}"),
        Err(e) => println!("Error: {e}"),
    };

如果文件不存在,会输出信息:

这个异常处理的过程不复杂,分为三步:

  1. 自定义的函数要返回Result<T,E>,

  2. 返回Result的函数时,后面加上问号,

  3. 在最上层,使用match处理结果。

但是这样是不够的,如果在一个大项目中,我们很难找到是哪个文件缺失了,rust不像c#那样,可以很容易的获取到出现问题的代码行数、类和方法名等。

最直观的方法是,在异常信息里,带上文件名。

自定义错误,带上文件名

rust自定义错误分为三步:

1)定义错误类型

2)实现Error特征(trait)

3)  实现Display特征

自定义错误的类型是enum, 和其他语言相比,这有点奇怪。 代码如下:

// 定义自定义错误类型
#[derive(Debug)]
pub enum MyError {
    FileOpenError(String),
    ParseError(String),
    Common(String),
}

// 实现Error特质
impl Error for MyError {}

// 实现Display特质以便打印错误信息
impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MyError::FileOpenError(msg) => write!(f, "Failed to open file: {}", msg),
            MyError::ParseError(msg) => write!(f, "Parse error: {}", msg),
            MyError::Common(msg) => write!(f, "Other error: {}", msg),
        }
    }
}

这时,读取文件的函数代码要改成这样:

fn read_file_to_string(file_path: String) -> Result<String, MyError>{
    let r = File::open(file_path.clone());
    match r {
        Ok(mut file) => {
            let mut contents = String::new();
            let r2 = file.read_to_string(&mut contents);
            match r2 {
                Ok(size) => return Ok( contents),
                Err(e) => return Err(MyError::Common(format!("{e} 文件: {file_path}"))),
            } 
        },
        Err(e) => {
            return Err(MyError::FileOpenError(format!("{e} 文件: {file_path}")));
        },
    }
}

代码变得很啰嗦,好在能比较好的显示错误了:

自定义错误的三部曲,虽然有点长,但是这是项目的公共代码,还是可以忍受的。读取文件的代码,和 c#比起来,真的太罗嗦了。

简化通用异常处理

读取文件内容的函数,代码罗嗦的原因是,异常类型通过问号匹配到自定义的MyError很麻烦。

这里我们采用一种更通用的方式,来处理异常:

1) 重新定义异常类型,并且提供其他异常向自定义异常转换的方法

custom_error.rs:

use std::error::Error;
use std::fmt;
use std::fmt::Display;

// 自定义错误类型,包含文件路径信息
#[derive(Debug)]
pub struct MyError {
    msg: String,
    source: String,
}

// 为自定义错误类型实现Error trait
impl Error for MyError {}

// 实现Display trait,以便于打印错误信息
impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}: {}", self.source, self.msg)
    }
}

pub fn convert_error(msg:String, err: String) -> MyError {
    MyError {
        msg: msg ,
        source: err.to_string(),
    }
}

// 定义一个新的trait
pub trait MyErrorExtension<T> {
    fn ex_err(self, msg:&String)->  Result<T, MyError>;
}

// 为Result<T,E>类型实现MyExtension trait
impl<T,E:Display> MyErrorExtension<T> for Result<T,E> {
    fn ex_err(self, msg:&String) ->  Result<T, MyError> {
        match self {
            Ok(t) => Ok(t),
            Err(e) => Err(MyError{msg:msg.to_string(), source: e.to_string()}),
        }
    }
}

2) 定义带有通用异常处理能力的函数的示例:

fn read_file_to_string(file_path: String) -> Result<String, MyError>{
    let context_info = format!("文件路径: {file_path}");
    fs::metadata(&file_path).ex_err(&context_info)?;
    let mut file = File::open(&file_path).ex_err( &context_info)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).ex_err(&context_info)?;
    Ok(contents)
}

以打开文件的方法为例,原本的调用是:

let mut file = File::open(&file_path)?;

新的调用,后面附加了重要的上下文信息,并且把异常类型转换为MyError:

let mut file = File::open(&file_path).ex_err( &context_info)?;

通过扩展方法ex_err, 达到了我们的目的。

标签:mut,MyError,String,file,msg,path,错误处理,Rust
From: https://www.cnblogs.com/ybst/p/18182038

相关文章

  • Rust | 实现 API 限速操作 Example
    在这篇文章中,我们将讨论如何在Rust中实现API限速。当涉及到生产中的服务时,是为了确保不良行为者不会滥用API——这就是API限速的作用所在。我们将实现“滑动窗口”算法,通过动态周期来检查请求历史,并使用基本的内存hashmap来存储用户IP及其请求时间。我们还将研究如......
  • rust搭建交叉编译环境
    最近尝试了一下rust交叉编译,简单记录一下。原理1、使用rust的编译器将rust源码编译到汇编或者.o的状态(具体是汇编还是.o没有考证过)。2、使用目标平台的toolchain将rust生成的汇编或者.o链接成ELF等可执行的格式。基于上述原理,需要解决两个问题:首先,怎么让rust将rust代......
  • rust trait 关联类型和泛型的区别
    关联类型和泛型虽然在某些方面看起来相似,但它们在Rust中扮演着不同的角色,有着本质的区别。下面我会详细解释关联类型、泛型以及它们在Iteratortrait上的应用,以帮助理解为什么Iteratortrait使用关联类型而非泛型参数来定义。关联类型关联类型是trait的一部分,它允许trait......
  • Camunda 流程执行错误处理ERROR BOUNDARY EVENT
     ERRORBOUNDARYEVENT:在任务发生异常时候会触发走,在代码中必须显式抛出thrownewBpmnError("error.....");publicvoidexecute(DelegateExecutiondelegateExecution)throwsException{System.out.println("进来了>>>>>>>>>>>>&......
  • Trusted Types API
    TrustedTypesAPI:锁定DOMAPI的不安全部分,以防止客户端跨站脚本(XSS)攻击untrusted<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width......
  • rust+stm32+vscode搭建开发调试环境
    1.安装rustrust官网传送门2.安装openocd安装openocd传送门3.安装stlink安装stlink传送门4.搭建gcc-arm-none-eabi编译环境搭建gcc-arm-none-eabi编译环境5.安装vscodevscode官网传送门6.安装相关插件rust-analyzer:使用VSCode开发Rust必备cortex-debug:调试、debug嵌入......
  • Rust中的并发性:Sync 和 Send Traits
    在并发的世界中,最常见的并发安全问题就是数据竞争,也就是两个线程同时对一个变量进行读写操作。但当你在SafeRust中写出有数据竞争的代码时,编译器会直接拒绝编译。那么它是靠什么魔法做到的呢?这就不得不谈Send和Sync这两个标记trait了,实现Send的类型可以在多线程间转......
  • Go语言系列——Go协程、信道(channel)、缓冲信道和工作池、Select、Mutex、结构体取代类
    文章目录21-Go协程Go协程是什么?Go协程相比于线程的优势如何启动一个Go协程?启动多个Go协程22-信道(channel)什么是信道?信道的声明通过信道进行发送和接收发送与接收默认是阻塞的信道的代码示例信道的另一个示例死锁单向信道关闭信道和使用forrange遍历信道23-缓冲信......
  • rust模块管理示例1
    1、创建如下rust工程不用管其中代码的作用是什么,只要知道有一个main.rs和四个模块s1、s2、s3、s4即可。2、s1模块使用了Rust2015的模块格式,即:需要创建s1文件夹,在s1下创建功能文件hello.rs及名为mod.rs的模块定义文件。s1/hello.rspubfnsay_hello(){println!("hellofr......
  • 代码统计利器:Rust tokei 库全面介绍
    引言作为程序员,我们常常需要统计项目中的代码行数,以了解项目规模和进度。市面上有很多代码统计工具,但不少工具存在统计不准、语言支持不全、性能不佳等问题。今天给大家介绍一个Rust生态中的代码统计利器:tokei。tokei通过语法分析准确统计代码行数,目前已支持200+种语言,而且......