Module std::result1.0.0[][src]

Expand description

Result 类型的错误处理。

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

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

只要预期到错误并且可以恢复,函数就返回 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),
}
Run

在简单情况下,在 Result 上进行模式匹配非常简单明了,但是 Result 附带了一些方便的方法,使使用它更加简洁。

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

// `is_ok` 和 `is_err` 方法按照他们说的做。
assert!(good_result.is_ok() && !good_result.is_err());
assert!(bad_result.is_err() && !bad_result.is_ok());

// `map` 消耗 `Result` 并产生另一个。
let good_result: Result<i32, i32> = good_result.map(|i| i + 1);
let bad_result: Result<i32, i32> = bad_result.map(|i| i - 1);

// 使用 `and_then` 继续计算。
let good_result: Result<bool, i32> = good_result.and_then(|i| Ok(i == 11));

// 使用 `or_else` 处理该错误。
let bad_result: Result<i32, i32> = bad_result.or_else(|i| Ok(i + 20));

// 消费结果并用 `unwrap` 返回内容。
let final_awesome_result = good_result.unwrap();
Run

必须使用结果

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

考虑 Write trait 为 I/O 类型定义的 write_all 方法:

use std::io;

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

Note: Write 的实际定义使用 io::Result,它只是 Result 的同义词。io::Error>.

该方法不会产生值,但是写入可能会失败。处理错误情况至关重要,并且 不要 编写类似以下内容的代码:

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

let mut file = File::create("valuable_data.txt").unwrap();
// 如果 `write_all` 错误,那么我们将永远不会知道,因为返回值将被忽略。
//
file.write_all(b"important message");
Run

如果您确实将其写在 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");
Run

您可能还简单地宣称成功:

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

或者使用 ? 在调用栈中传播错误:

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

问号运算符, ?

在编写调用许多返回 Result 类型的函数的代码时,错误处理可能很乏味。 问号运算符 ? 在调用栈中隐藏了一些传播错误的样板。

它将替换为:

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 = 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(())
}
Run

有了这个:

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")?;
    // 尽早返回错误
    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(())
}
Run

好多了!

? 结束表达式将得到未包装的成功 (Ok) 值,除非结果为 Err,在这种情况下,Err 会从封闭的函数中提前返回。

? 只能在返回 Result 的函数中使用,因为它提供了 Err 的较早返回。

方法概述

除了使用模式匹配,Result 还提供了多种不同的方法。

查询变量

如果 Result 分别为 OkErr,则 is_okis_err 方法返回 true

用于处理引用的适配器

  • as_ref&Result<T, E> 转换为 Result<&T, &E>
  • as_mut&mut Result<T, E> 转换为 Result<&mut T, &mut E>
  • as_deref&Result<T, E> 转换为 Result<&T::Target, &E>
  • as_deref_mut&mut Result<T, E> 转换为 Result<&mut T::Target, &mut E>

提取包含的值

当它是 Ok 成员时,这些方法提取 Result<T, E> 中包含的值。如果 ResultErr:

panicking 方法 expectunwrap 需要 E 来实现 Debug trait。

当它是 Err 成员时,这些方法提取 Result<T, E> 中包含的值。他们需要 T 来实现 Debug trait。如果 ResultOk:

转换包含的值

这些方法将 Result 转换为 Option:

此方法转换 Ok 成员的包含值:

此方法转换 Err 成员的包含值:

这些方法将 Result<T, E> 转换为可能不同类型 U 的值:

  • map_or 将提供的函数应用于 Ok 的包含值,如果 Result 是,则返回提供的默认值 Err
  • map_or_else 将提供的函数应用于 Ok 的包含值,或将提供的回退函数应用于 Err 的包含值

布尔运算符

这些方法将 Result 视为布尔值,其中 Ok 的作用类似于 true,而 Err 的作用类似于 false。这些方法有两类: 一类以 Result 作为输入,一类以函数作为输入 (延迟评估)。

andor 方法将另一个 Result 作为输入,并生成一个 Result 作为输出。and 方法可以生成具有与 Result<T, E> 不同的内部类型 UResult<U, E> 值。 or 方法可以生成具有与 Result<T, E> 不同的错误类型 FResult<T, F> 值。

methodselfinputoutput
andErr(e)(ignored)Err(e)
andOk(x)Err(d)Err(d)
andOk(x)Ok(y)Ok(y)
orErr(e)Err(d)Err(d)
orErr(e)Ok(y)Ok(y)
orOk(x)(ignored)Ok(x)

and_thenor_else 方法将函数作为输入,并且仅在需要产生新值时才评估函数。and_then 方法可以生成具有与 Result<T, E> 不同的内部类型 UResult<U, E> 值。 or_else 方法可以生成具有与 Result<T, E> 不同的错误类型 FResult<T, F> 值。

methodselffunction inputfunction resultoutput
and_thenErr(e)(not provided)(not evaluated)Err(e)
and_thenOk(x)xErr(d)Err(d)
and_thenOk(x)xOk(y)Ok(y)
or_elseErr(e)eErr(d)Err(d)
or_elseErr(e)eOk(y)Ok(y)
or_elseOk(x)(not provided)(not evaluated)Ok(x)

迭代 Result

可以对 Result 进行迭代。如果您需要一个条件为空的迭代器,这会很有帮助。迭代器将产生单个值 (当 ResultOk 时),或不产生任何值 (当 ResultErr 时)。 例如,如果 ResultOk(v),则 into_iter 的作用类似于 once(v); 如果 ResultErr,则它的作用类似于 empty()

Result<T, E> 上的迭代器分为三种类型:

  • into_iter 消耗 Result 并生成包含的值
  • iter 对包含的值产生类型为 &T 的不支持引用
  • iter_mut 产生一个 &mut T 类型的引用引用到包含的值

有关这如何有用的示例,请参见 迭代 Option

您可能希望使用迭代器链来执行可能失败的操作的多个实例,但希望在继续处理成功结果的同时忽略失败。 在本例中,我们利用 Result 的可迭代特性,使用 flatten 仅选择 Ok 值。

let mut results = vec![];
let mut errs = vec![];
let nums: Vec<_> = vec!["17", "not a number", "99", "-27", "768"]
   .into_iter()
   .map(u8::from_str)
   // 保存原始 `Result` 值的克隆以进行检查
   .inspect(|x| results.push(x.clone()))
   // 挑战: 解释这如何仅捕获 `Err` 值
   .inspect(|x| errs.extend(x.clone().err()))
   .flatten()
   .collect();
assert_eq!(errs.len(), 3);
assert_eq!(nums, [17, 99]);
println!("results {:?}", results);
println!("errs {:?}", errs);
println!("nums {:?}", nums);
Run

收集到 Result

Result 实现 FromIterator trait,它允许将 Result 值上的迭代器收集到原始 Result 值的每个包含值的集合的 Result 中,或者如果任何元素是 Err,则为 Err

let v = vec![Ok(2), Ok(4), Err("err!"), Ok(8)];
let res: Result<Vec<_>, &str> = v.into_iter().collect();
assert_eq!(res, Err("err!"));
let v = vec![Ok(2), Ok(4), Ok(8)];
let res: Result<Vec<_>, &str> = v.into_iter().collect();
assert_eq!(res, Ok(vec![2, 4, 8]));
Run

Result 还实现了 ProductSum traits,允许对 Result 值的迭代器提供 productsum 方法。

let v = vec![Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")];
let res: Result<i32, &str> = v.into_iter().sum();
assert_eq!(res, Err("error!"));
let v: Vec<Result<i32, &str>> = vec![Ok(1), Ok(2), Ok(21)];
let res: Result<i32, &str> = v.into_iter().product();
assert_eq!(res, Ok(42));
Run

Structs

IntoIter

ResultOk 成员中的值的迭代器。

Iter

ResultOk 成员的引用上的迭代器。

IterMut

ResultOk 成员的可变引用上的迭代器。

Enums

Result

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