Rust标准库中的结果(result)介绍

Rust标准库中的结果(result)介绍

https://doc.rust-lang.org/std/result/index.html

使用Result类型的错误处理。

Result<T, E>是用于返回和传播错误的类型。它是一个枚举,其中Ok(T)代表成功并包含一值,Err(E)代表错误并包含错误值。

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

函数在预计到错误并可恢复时返回Result。在 std crate 中,Result 最显著的作用是用于 I/O。

一个简单的返回Result的函数可以这样定义和使用:

#[derive(Debug)]
enum Version { Version1, Version2 }

fn parse_version(header: &[u8]) -> Result<Version, &'static str> {
    match header.get(0) {
        None => Err("invalid header length"),
        Some(&1) => Ok(Version::Version1),
        Some(&2) => Ok(Version::Version2),
        Some(_) => Err("invalid version"),
    }
}

let version = parse_version(&[1, 2, 3, 4]);
match version {
    Ok(v) => println!("working with version: {:?}", v),
    Err(e) => println!("error parsing header: {:?}", e),
}

在Result上的模式匹配对于简单的案例来说是很清晰和直接的,但是Result自带的一些方便的方法可以让工作更简洁。

let good_result: Result<i32, i32> = Ok(10);
let bad_result: Result<i32, i32> = Err(10);

// The `is_ok` and `is_err` methods do what they say.
assert!(good_result.is_ok() && !good_result.is_err());
assert!(bad_result.is_err() && !bad_result.is_ok());

// `map` consumes the `Result` and produces another.
let good_result: Result<i32, i32> = good_result.map(|i| i + 1);
let bad_result: Result<i32, i32> = bad_result.map(|i| i - 1);

// Use `and_then` to continue the computation.
let good_result: Result<bool, i32> = good_result.and_then(|i| Ok(i == 11));

// Use `or_else` to handle the error.
let bad_result: Result<i32, i32> = bad_result.or_else(|i| Ok(i + 20));

// Consume the result and return the contents with `unwrap`.
let final_awesome_result = good_result.unwrap();

结果必须使用

使用返回值来表示错误,一个常见的问题是很容易忽略返回值,从而无法处理错误。Result被注释了 #[must_use] 属性,当忽略了Result值时,编译器会发出警告。这使得Result对于可能会遇到错误但不会返回有用值的函数特别有用。

考虑一下Write属性为I/O类型定义的write_all方法。

use std::io;

trait Write {
    fn write_all(&mut self, bytes: &[u8]) -> Result<(), io::Error>;
}

注意:Write的实际定义使用的是io::Result,它只是Result<T, io::Error>的同义词。

这个方法不会产生一个值,但是写的时候可能会失败。关键是要处理好错误的情况,不要这样写:

use std::fs::File;
use std::io::prelude::*;

let mut file = File::create("valuable_data.txt").unwrap();
// If `write_all` errors, then we'll never know, because the return
// value is ignored.
file.write_all(b"important message");

如果你真的在Rust中写了,编译器会给你一个警告(默认情况下,由 unused_must_use lint 控制)。

相反,如果你不想处理这个错误,你可以直接用 expect 来断言成功。如果写入失败了,这将会panic,并提供一个略微有用的消息来说明原因。

use std::fs::File;
use std::io::prelude::*;

let mut file = File::create("valuable_data.txt").unwrap();
file.write_all(b"important message").expect("failed to write message");

也可以简单地断言成功:

assert!(file.write_all(b"important message").is_ok());

或者使用 ? 将错误传播到调用栈上:

fn write_message() -> io::Result<()> {
    let mut file = File::create("valuable_data.txt")?;
    file.write_all(b"important message")?;
    Ok(())
}

问号运算符?

当编写调用许多返回结果类型的函数的代码时,错误处理可能会很繁琐。问号操作符 ? 隐藏了一些在调用堆栈中传播错误的繁文缛节。

下面这段代码:

use std::fs::File;
use std::io::prelude::*;
use std::io;

struct Info {
    name: String,
    age: i32,
    rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
    // Early return on error
    let mut file = match File::create("my_best_friends.txt") {
           Err(e) => return Err(e),
           Ok(f) => f,
    };
    if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) {
        return Err(e)
    }
    if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) {
        return Err(e)
    }
    if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) {
        return Err(e)
    }
    Ok(())
}

将被替代为:

use std::fs::File;
use std::io::prelude::*;
use std::io;

struct Info {
    name: String,
    age: i32,
    rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
    let mut file = File::create("my_best_friends.txt")?;
    // Early return on error
    file.write_all(format!("name: {}\n", info.name).as_bytes())?;
    file.write_all(format!("age: {}\n", info.age).as_bytes())?;
    file.write_all(format!("rating: {}\n", info.rating).as_bytes())?;
    Ok(())
}

这样就好很多了!

以 ? 结束的表达式将导致成功(Ok)值的 unwrap,除非结果是Err,在这种情况下,Err会从包围函数中提前返回。

?只能用于返回 Result 的函数中,因为它提供了 Err 的提前返回。

Result Enum

#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

Result是一种类型,代表成功(Ok)或失败(Err)。

变量:

  • Ok(T): 包含成功值
  • Err(E):包含错误值

map_or 方法

对包含的值(如果有的话)应用一个函数,或者返回提供的默认值(如果没有的话)。

传递给map_or的参数会被立即求值;如果你传递的是函数调用的结果,建议使用map_or_else,它是延迟求值。

let x: Result<_, &str> = Ok("foo");
assert_eq!(x.map_or(42, |v| v.len()), 3);

let x: Result<&str, _> = Err("bar");
assert_eq!(x.map_or(42, |v| v.len()), 42);

map_or_else方法

通过将一个结果<T, E>映射到U,通过将一个函数应用到包含的Ok值,或者将一个fallback函数应用到包含的Err值。

这个函数可以用来在处理错误时解包一个成功的结果。

let k = 21;

let x : Result<_, &str> = Ok("foo");
assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);

let x : Result<&str, _> = Err("bar");
assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);