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

Posted Jtag特工

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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语言教程 - 错误处理和可选值的主要内容,如果未能解决你的问题,请参考以下文章

请求完成处理程序致命错误:在展开可选值异常时意外发现 nil

如何防止致命错误:在展开可选值时意外发现 nil

NSEntityDescription 在展开可选值时返回 nil

无法过去 - 线程 1:致命错误:在展开可选值时意外发现 nil [重复]

SWIFT - 致命错误:在展开可选值时意外发现 nil

在 NSCoder 中展开可选值时意外发现 nil