Rust Result 可恢复错误

发布时间: 更新时间: 总字数:949 阅读时间:2m 作者: IP上海 分享 网址

Rust使用Result<T, E>定义可恢复错误。

介绍

Result 枚举的定义

enum Result<T, E> {
    Ok(T),
    Err(E),
}

说明:

  • T/E为泛型
  • T:操作成功时,对应 Ok 变体里返回的数据类型
  • E:操作失败时,对应 Err 变体里返回的数据类型

示例

Result 使用 match 表达式处理,且 Result 及其变体有 prelude 导入作用域

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("foo.md");
    let f = match f {
        Ok(file) => file,
        // Err(error) => {
        //     panic!("Error open file {:?}", error);  // Error open file Os { code: 2, kind: NotFound, message: "No such file or directory" }
        // }

        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("foo.md") {
                Ok(fc) => fc,
                Err(error) => panic!("create file err: {:?}", error),
            },
            o => panic!("err: {:?}", o),
        },
    };

    println!("{:?}", f);
}

说明:

  • 上述示例使用很多 match,比较麻烦。可以使用闭包(closure)优化代码
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("foo.md").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("foo.md").unwrap_or_else(|error| {
                panic!("create file err: {:?}", error);
            })
        } else {
            panic!("err: {:?}", error);
        }
    });

    println!("{:?}", f);
}

unwrap

unwrap 是 match 表达式的一个快捷方法:

  • 若 Result 结果是 Ok,返回 Ok 里的值
  • 若 Result 结果是 Err,调用 panic! 宏,且错误不可以自定义
use std::fs::File;

fn main() {
    let f = File::open("foo.md").unwrap();
    println!("{:?}", f);
}

expect

expect 和 unwrap 类似,且支持指定错误信息

use std::fs::File;

fn main() {
    let f = File::open("foo.md").expect("open file foo.md err");
    println!("{:?}", f);
}

传播错误

在函数中错误处理有两种方式:

  • 函数中处理错误
  • 将错误返回给函数调用者
use std::fs::File;
use std::io;
use std::io::Read;

fn read_user_from_file() -> Result<String, io::Error> {
    let f = File::open("foo.md");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

fn main() {
    let content = read_user_from_file();
    match content {
        Ok(c) => println!("{}", c),
        Err(e) => println!("err: {}", e),
    }
}

? 运算符

Rust 中使用 ? 运算符 来快捷的传播错误,上面的例子,通过 ? 运算符 重构如下:

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

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

fn main() {
    let content = read_user_from_file();
    match content {
        Ok(c) => println!("{}", c),
        Err(e) => println!("err: {}", e),
    }
}

? 运算符 作用如下:

  • 如果 Result 是 Ok(),将 Ok 里的值作为表达式的结果,程序继续执行
  • 如果 Result 是 Err(e),则 Err(e) 作为整个函数的返回值

? 运算符from 函数

  • Trait std::convert::Fromfrom 函数 用于错误之间的转换
  • ? 所应用的错误,会隐式的备 from 函数处理
    • 当 ? 调用 from 函数时,它所接收的错误类型会被转化为当前函数返回类型所定义的错误类型
  • 因此,常用于针对不同错误原因,返回同一种错误类型,且该错误类型需要实现目标错误类型的 from 函数

链式调用

上述示例重新优化,如下:

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

fn read_user_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("foo.md")?.read_to_string(&mut s)?;
    Ok(s)
}

fn main() {
    let content = read_user_from_file();
    match content {
        Ok(c) => println!("{}", c),
        Err(e) => println!("err: {}", e),
    }
}

说明:

  • ? 运算符 只能用于返回 Result 的函数,注意:
    • 默认 fn main() {} 函数的返回值为 (),因此不能在 main() 函数中使用 ? 运算符
    • main() 函数可以指定返回值,此时可以在 main() 函数中使用 ? 运算符,示例:
use std::fs::File;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let f = File::open("foo.md")?;
    println!("{:?}", f);
    Ok(())
}

说明:

  • Box<dyn Error> 为 trait 对象,可以理解为任何可能的错误类型
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数