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>;
}
RunNote: Write
的实际定义使用了 io::Result
,它只是 Result<T, 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
分别为 Ok
或 Err
,则 is_ok
和 is_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>
中包含的值。如果 Result
是 Err
:
expect
panics 带有提供的自定义消息unwrap
panics 带有泛型信息unwrap_or
返回提供的默认值unwrap_or_default
返回类型T
的默认值 (必须实现Default
trait)unwrap_or_else
返回对提供的函数求值的结果
panicking 方法 expect
和 unwrap
需要 E
来实现 Debug
trait。
当它是 Err
变体时,这些方法提取 Result<T, E>
中包含的值。他们需要 T
来实现 Debug
trait。如果 Result
是 Ok
:
expect_err
panics 带有提供的自定义消息unwrap_err
panics 带有泛型信息
转换包含的值
err
transformsResult<T, E>
intoOption<E>
, mappingErr(e)
toSome(e)
andOk(v)
toNone
ok
transformsResult<T, E>
intoOption<T>
, mappingOk(v)
toSome(v)
andErr(e)
toNone
transpose
transposes aResult
of anOption
into anOption
of aResult
此方法转换 Ok
变体中包含的值:
map
通过将提供的函数应用于Ok
的包含值并保持Err
值不变,将Result<T, E>
转换为Result<U, E>
此方法转换 Err
变体中包含的值:
map_err
通过将提供的函数应用于Err
的包含值并保持Ok
值不变,将Result<T, E>
转换为Result<T, F>
这些方法将 Result<T, E>
转换为可能不同类型 U
的值:
map_or
将提供的函数应用于Ok
的包含值,或者如果Result
是返回提供的默认值Err
map_or_else
applies the provided function to the contained value ofOk
, or applies the provided default fallback function to the contained value ofErr
布尔运算符
这些方法将 Result
视为布尔值,其中 Ok
的作用类似于 true
,而 Err
的作用类似于 false
。这些方法有两类:一类以 Result
作为输入,一类以函数作为输入 (延迟评估)。
and
和 or
方法将另一个 Result
作为输入,并生成一个 Result
作为输出。and
方法可以生成具有与 Result<T, E>
不同的内部类型 U
的 Result<U, E>
值。
or
方法可以生成具有与 Result<T, E>
不同的错误类型 F
的 Result<T, F>
值。
method | self | input | output |
---|---|---|---|
and | Err(e) | (ignored) | Err(e) |
and | Ok(x) | Err(d) | Err(d) |
and | Ok(x) | Ok(y) | Ok(y) |
or | Err(e) | Err(d) | Err(d) |
or | Err(e) | Ok(y) | Ok(y) |
or | Ok(x) | (ignored) | Ok(x) |
and_then
和 or_else
方法将函数作为输入,并且仅在需要产生新值时才评估函数。and_then
方法可以生成一个 [Result<U,E>
] 值,该值的内部类型 U
与 [Result<T,E>
] 不同。
or_else
方法可以生成具有与 Result<T, E>
不同的错误类型 F
的 Result<T, F>
值。
method | self | function input | function result | output |
---|---|---|---|---|
and_then | Err(e) | (not provided) | (not evaluated) | Err(e) |
and_then | Ok(x) | x | Err(d) | Err(d) |
and_then | Ok(x) | x | Ok(y) | Ok(y) |
or_else | Err(e) | e | Err(d) | Err(d) |
or_else | Err(e) | e | Ok(y) | Ok(y) |
or_else | Ok(x) | (not provided) | (not evaluated) | Ok(x) |
比较运算符
如果 T
和 E
都实现 PartialOrd
,那么 Result<T, E>
将派生其 PartialOrd
实现。
按照此顺序,一个 Ok
的比较小于任何 Err
,而两个 Ok
或两个 Err
的比较与其包含的值分别在 T
或 E
中进行比较。
如果 T
和 E
都实现了 Ord
,那么 Result<T, E>
也实现了。
assert!(Ok(1) < Err(0));
let x: Result<i32, ()> = Ok(0);
let y = Ok(1);
assert!(x < y);
let x: Result<(), i32> = Err(0);
let y = Err(1);
assert!(x < y);
Run迭代 Result
可以对 Result
进行迭代。如果您需要一个条件为空的迭代器,这会很有帮助。迭代器要么产生单个值 (当 Result
为 Ok
时),要么不产生任何值 (当 Result
为 Err
时)。
例如,如果 Result
是 Ok(v)
,则 into_iter
的作用类似于 once(v)
; 如果 Result
是 Err
,则它的作用类似于 empty()
。
Result<T, E>
上的迭代器分为三种类型:
有关这如何有用的示例,请参见 迭代 Option
。
您可能希望使用迭代器链来执行可能失败的操作的多个实例,但希望在继续处理成功结果的同时忽略失败。
在本例中,我们利用 Result
的可迭代特性,使用 flatten
仅选择 Ok
值。
let mut results = vec![];
let mut errs = vec![];
let nums: 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 = [Ok(2), Ok(4), Err("err!"), Ok(8)];
let res: Result<Vec<_>, &str> = v.into_iter().collect();
assert_eq!(res, Err("err!"));
let v = [Ok(2), Ok(4), Ok(8)];
let res: Result<Vec<_>, &str> = v.into_iter().collect();
assert_eq!(res, Ok(vec![2, 4, 8]));
RunResult
还实现了 Product
和 Sum
traits,允许对 Result
值的迭代器提供 product
和 sum
方法。
let v = [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 = [Ok(1), Ok(2), Ok(21)];
let res: Result<i32, &str> = v.into_iter().product();
assert_eq!(res, Ok(42));
Run