gpt4 book ai didi

rust - 如何处理 flat_map 中的结果

转载 作者:行者123 更新时间:2023-12-03 11:26:01 30 4
gpt4 key购买 nike

我知道我们可以使用 collect移动 Result从内到外,例如:

fn produce_result(my_struct: &MyStruct) -> Result<MyStruct, Error>;

let my_results: Vec<MyStruct> = vec![];
let res = my_results.iter().map(|my_struct| produce_result(&my_struct)).collect::<Result<Vec<MyStruct>, Error>>;

它将错误从闭包传播到外部。

但是,此方法不适用于 flat_map案例( Rust playground ):

fn produce_result(my_struct: &MyStruct) -> Result<Vec<MyStruct>, Error>;

let my_results: Vec<MyStruct> = vec![];
let res = my_results.iter().flat_map(|my_struct| produce_result(&my_struct)).collect::<Result<Vec<MyStruct>, Error>>;

编译器提示:“不能从 std::result::Result<std::vec::Vec<MyStruct>, Error> 类型的元素上的迭代器构建 std::vec::Vec<MyStruct> 类型的集合”

如何解决这种情况?

最佳答案

flat_map通过调用它的 IntoIterator 来“展平”从闭包返回的值的顶层。执行。重要的是它不会试图进入内部 - 即,如果您有自己的 MyResult ,它会在 flat_map 上出错本身:

enum Error {}

enum MyResult<T, U> {
Ok(T),
Err(U),
}

struct MyStruct;

fn produce_result(item: &MyStruct) -> MyResult<Vec<MyStruct>, Error> {
MyResult::Ok(vec![])
}

fn main() {
let my_structs: Vec<MyStruct> = vec![];
let res = my_structs
.iter()
.flat_map(|my_struct| produce_result(&my_struct))
.collect::<Result<Vec<MyStruct>, Error>>();
}

( Playground )

错误:
error[E0277]: `MyResult<std::vec::Vec<MyStruct>, Error>` is not an iterator
--> src/main.rs:18:10
|
18 | .flat_map(|my_struct| produce_result(&my_struct))
| ^^^^^^^^ `MyResult<std::vec::Vec<MyStruct>, Error>` is not an iterator
|
= help: the trait `std::iter::Iterator` is not implemented for `MyResult<std::vec::Vec<MyStruct>, Error>`
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `MyResult<std::vec::Vec<MyStruct>, Error>`

但是,在您的情况下,行为是不同的,因为 Result implements IntoIterator .这个迭代器产生 Ok值不变并跳过 Err ,所以当 flat_map ping Result ,您可以有效地忽略每个错误并仅使用成功调用的结果。

有一种方法可以修复它,虽然很麻烦。您应该明确匹配 Result ,包装 Err Vec 中的案例并“分发” Ok已经存在的案例 Vec ,然后让 flat_map做它的工作:
let res = my_structs
.iter()
.map(|my_struct| produce_result(&my_struct))
.flat_map(|result| match result {
Ok(vec) => vec.into_iter().map(|item| Ok(item)).collect(),
Err(er) => vec![Err(er)],
})
.collect::<Result<Vec<MyStruct>, Error>>();

Playground

还有另一种方法,如果确实存在错误(即使只是有时),它的性能可能会更高:
fn external_collect(my_structs: Vec<MyStruct>) -> Result<Vec<MyStruct>, Error> {
Ok(my_structs
.iter()
.map(|my_struct| produce_result(&my_struct))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect())
}

Playground

我做了一些快速的基准测试 - 代码是 on the playground也一样,尽管由于 cargo bench 的缺失而无法在那里运行命令,所以我在本地运行它们。结果如下:
test vec_result::external_collect_end_error   ... bench:   2,759,002 ns/iter (+/- 1,035,039)
test vec_result::internal_collect_end_error ... bench: 3,502,342 ns/iter (+/- 438,603)

test vec_result::external_collect_start_error ... bench: 21 ns/iter (+/- 6)
test vec_result::internal_collect_start_error ... bench: 30 ns/iter (+/- 19)

test vec_result::external_collect_no_error ... bench: 7,799,498 ns/iter (+/- 815,785)
test vec_result::internal_collect_no_error ... bench: 3,489,530 ns/iter (+/- 170,124)

似乎有两个链接的版本 collect s 使用嵌套的方法的两倍时间 collect s 如果执行成功,但当执行因某些错误而短路时,速度实质上(大约三分之一,大约)更快。这个结果在多次基准测试中是一致的,所以报告的大差异可能并不重要。

关于rust - 如何处理 flat_map 中的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59852161/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com