Rust错误处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust错误处理相关的知识,希望对你有一定的参考价值。

参考技术A 简单的错误处理

要不是使用“?”,Rust的错误处理会显得有些不够灵巧。要使用“?”我们需要声明返回值类型为Result类型,这种类型可以包含任何具备std::error::Error特征从而可以转换为Bax<Error>类型的错误类型。

拿我们需要处理IO错误和字符串转换为数字错误举例:

use std::fs::File;

use std::io::prelude::*;

use std::error::Error;

fn run(file: &str) -> Result<i32, Box<Error>>

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

  let mut contents = String::new();

  file.read_to_string(&mut contents)?;

  Ok(contents.trim().parse()?)



这里使用了俩个"?"处理可能发生的IO错误:打开文件错误和读取内容为string错误。

使用了一个“?”处理可能发生的类型转换错误。

最后我们将结果包装为Ok类型。Rust可以中返回值中判断出parse的结果为i32类型。

简化Result类型的声明比较容易,我们可以定义一个自己的Result类型,比如:

type BoxResult<T> = Result<T, Box<Error>>

但是,我们程序还需要自定义Error类型,那我们就需要做些其他工作:

可以实现Debug特征

必须实现Display特征

必须实现Error特征

就像这样:

//error1.rs

use std::error::Error;

use std::fmt;

#[derive(Debug)]

struct MyError

  details: String



impl MyError

        fn new(msg: &str) -> MyError

              MyErrordetails: msg.to_string()

       



impl fnt::Display for MyError

        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Reuslt

              write!(f, "", self.details)

     



impls Error for MyError

      fn description(&self)  -> &str

                  &self.details

     



//一个测试我们自定义错误的函数

fn raises_my_error(yes: bool) -> Result<(), MyError>

      if yes

              Err(MyError::new("borked"))

      else

                Ok(())

     



输入Result<T, MyError>比较麻烦,所以很多模块都定义了自己的Result类型,这样可以少敲几下键盘。比如IO模块定义了io::Reust<T>代替Result<T, io::Error>使用。

在下面的例子中演示的是怎么处理String类型转化为浮点数类型可能出现错误的情况。

现在我们知道使用"?"可以方便的把有错误的表达式转换为Err返回。这种转换是通过From特征实现的。

你可以继续使用这种方便的转换,这在一些比较简单的应用中是个不错的选择,接下来我们演示的场景会复杂一些。

ParseFloatError实现了Error特征,所以它具备description()方法。

use std::num::ParseFloatError;impl From for MyError       fn from(err: ParseFloatError) -> Self                 MyError::new(err.description())      fn parse_f64(s: &str,  yes: bool) -> Result         raise_my_error(yes)?;        let x: f64 = s.parse()?        Ok(x)fn main()     println!(" :?", parse_f64("42", false));    println!(" :?", parse_f64("42", true));    println!(" :?", parse_64("?42", false));

执行结果会是这样:

Ok(42)

Err(MyError details: “borked”)

Err(MyError details: “invalid float literal”)

未完待续

原文地址

Rust语言教程 - 错误处理和可选值

Rust语言教程(6) - 错误处理和可选值

Rust的错误处理

从前面的学习中,我们对于Rust的错误处理应该已经有个体感了。Rust的返回值中,同时包含了正确情况下的值和错误情况下的报错信息的值。

以上一讲的读取标准输入值为例,我们其实没有处理返回值:

    let sin1 = std::io::stdin().read_line(&mut str8);

如果要处理的话,需要如何做呢?

使用match表达式处理错误

我们可以使用match表达式,来同时对正确和错误两种情况进行处理。
我们来看处理上面的返回值的例子:

    println!("Please input a number:");
    let mut str8 = String::with_capacity(255);
    let sin1 = std::io::stdin().read_line(&mut str8);
    match sin1 
        Ok(size1) =>
            println!("The size read from stdin is:",size1);
            let num8 = str8.trim().parse::<i32>().unwrap();
            println!("",num8);
        
        Err( err) =>
            println!("Read error!",err);
        
    

通过判断状态处理错误

如果觉得match表达式嵌套得太多了看起来烦,没关系,我们也可以直接通过判断状态来进行处理。状态不看就unwrap有点太武断了,早晚panic,加个is_ok的判断吧:

            let rnum = str8.trim().parse::<i32>();
            if rnum.is_ok()
                println!("",rnum.unwrap());
            

在这种情况下如何获取错误信息呢?我们可以通过err方法。但是,err方法返回的是一个Option可选值,与错误值类似,我们还需要加一重处理,我们来看例子:

            let rnum = str8.trim().parse::<i32>();
            if rnum.is_ok()
                println!("",rnum.unwrap());
            else
                let err1 = rnum.err();
                if err1.is_some()
                    println!("",rnum.err().unwrap());
                
            

错误时给个默认值

有的时候,有错误也不重要,我们给一个默认值就好。

比如上面的parse整数,如果失败就给个0值,提升容错性:

println!("",rnum.unwrap_or(0));

当然,如果只给个默认值,可能还不够,我们还想做一些进一步的操作。这时,我们可以给一个闭包,在闭包里实现更多的控制,比如可以打印个日志之类的:

            rnum.unwrap_or_else(|err| 
                return 0;
            );

panic时给出定制化的提示

像上面的输入的例子,如果用户给出的值不合法,将程序panic掉也是一种办法。
但是我们可以输出自己的提示,这样用户知道是什么问题,下一次输入的时候可以避免。

我们还是修改上面的例子:

            println!("The size read from stdin is:",size1);
            let rnum = str8.trim().parse::<i32>();
            rnum.expect("Please input an integer");

输出的panic信息如下:

thread 'main' panicked at 'Please input an integer: ParseIntError  kind: InvalidDigit ', src/main.rs:303:18
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
   1: core::panicking::panic_fmt
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:92:14
   2: core::option::expect_none_failed
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/option.rs:1268:5
   3: core::result::Result<T,E>::expect
             at /Users/lusinga/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:933:23
   4: tools::test
             at ./src/main.rs:303:13
   5: tools::main
             at ./src/main.rs:36:5
   6: core::ops::function::FnOnce::call_once
             at /Users/lusinga/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

复合类型

随着学习的深入,靠简单类型可能已经难以满足我们的需求了。我们现在可以引入一些复合类型来方便编程。

元组

同其它语言一样,Rust的元组tuple也是用括号括起来的一组可以为不同类型的值的组合。

我们来个例子:

    let t1 = (1.0, "Test");

要访问元素的话,可以使用数字做下标,比如第1个就是t1.0:

    let t1 = (1.0, "Test");
    println!(" ",t1.0,t1.1);

元组可以为空,()叫做unit。

结构体

元组只有个顺序,如果想给字段指定个名字的话,可以使用结构体。

比如我们定义实部和虚部构成的复数:

    struct Complex 
        real : i32,
        imagine : i32
    

初始化可以使用下面的方法:

let c1 = Complexreal :0, imagine: 1;

访问的话大家都很熟悉了:

println!("+i",c1.real,c1.imagine);

枚举enum

Rust的enum有点意思,用来表示可以是这种类型,也可以是另一种类型的类型。
比如我们可以定义一种既可能是数字又可能是字符串的类型:

    enum NumberOrText 
        Int(i32),
        Text(String)
    

其中的字段名可以用来初始化类型变量的值:

    enum NumberOrText 
        Int(i32),
        Text(String)
    

    let e1 = NumberOrText::Int(1);
    let e2 = NumberOrText::Text("1".to_string());

那么,这个有两种可能的类型如何处理呢?我们还是用match表达式来处理:

    match e1 
        NumberOrText::Int(value) => println!("",value),
        NumberOrText::Text(value) => println!("",value)
    

可选值

在Rust中,如果返回值可能为空,可以使用可选值来封装之。这样就可以避免很多其它语言可能出现的NullPointerException之类的错误。
前面介绍的enum虽然看起来有点奇怪,但是却是实现可选值的有力武器。
没错,Rust的Option类型就是用enum来实现的:

pub enum Option<T> 
    None,
    Some(T),

我们来看个例子,可能是整数,也可能是空值的类型,可以用Option等表示:

    let o1 : Option<i32> = Some(1_i32);
    let o2 : Option<i32> = None;

如何使用Option呢?我们可以使用is_some来判断是不是有值,有的话就可以unwrap:

    let o3 = Some(2_i32);
    if o3.is_some()
        println!("",o3.unwrap());
    

当然,不嫌烦的话,match表达式仍然是很好的选择:

    let o1 : Option<i32> = Some(1_i32);
    let v1 = match o1 
        Option::Some(value) => value,
        Option::None => 0_i32
    ;

同样,我们也可以用unwrap_or来指定默认值:

    let o2 : Option<i32> = None;
    println!("o2=",o2.unwrap_or(0));

小结

有了enum的基础,我们再回望一下错误处理的Result类型,它其实也是个enum:

pub enum Result<T, E> 
    Ok(T),
    Err(E),

以上是关于Rust错误处理的主要内容,如果未能解决你的问题,请参考以下文章

Rust Web 全栈开发之 Web Service 中的错误处理

如何使用适用于 DynamoDb 的 AWS Rust 开发工具包编写惯用的 Rust 错误处理?

Rust:从标准输入读取和映射行并处理不同的错误类型

Rust - 使用 Rayon 进行排列 - 向量内存分配错误

Rust编程语言入门之项目实例:- 命令行程序

Rust语言优化Python性能案例