在Rust中调用结果迭代器上的映射

我想以“函数式编程”风格编写一些代码。

但是,我从结果迭代器开始,我只想将该函数应用于Ok项目。此外,我想停止第一个错误的迭代(但是,我愿意接受不同的行为)。

到目前为止,我使用的是嵌套map()模式:<iter>.map(|l| l.map(replace))。我认为这是非常丑陋的。

使用每晚的“result_flattening”,我可以将每个嵌套Result<Result<T, E>, E>Result<T, E>. 使用eyre::ContextI 将不同的错误类型转换为eyre::Report错误类型。所有这一切都让人感觉很笨拙。

在 Rust 中写这个的优雅方式是什么?

最小工作示例

#![feature(result_flattening)]
use std::io::BufRead;

use eyre::Context;

fn main() {
    let data = std::io::Cursor::new(b"FFBFFFBLLLnBFBFBBFRLRnFFFBFFBLLL");

    let seats: Result<Vec<_>, _> = data
        .lines()
        .map(|l| l.map(replace).context("force eyre"))
        .map(|l| l.map(|s| u32::from_str_radix(&s, 2).context("force eyre")))
        .map(|r| r.flatten())
        .collect();

    println!("{:#?}", seats);
}

fn replace(line: String) -> String {
    line.replace('F', "0")
        .replace('B', "1")
        .replace('L', "0")
        .replace('R', "1")
}

进一步参考:

  • 当 Iterator::map 返回 Result::Err 时,如何停止迭代并返回错误?
  • 结果执行FromIter
  • result_flatten: https://doc.rust-lang.org/std/result/enum.Result.html?search=#method.flatten , https://github.com/rust-lang/rust/issues/70142 (我' m 使用 rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24))
  • lines()返回Results: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines

回答

由于您无论如何都会丢弃错误类型,因此您可以eyre完全避免并使用.ok将 the 转换Result为 an Option,然后只需使用Option'sand_then以避免每次都变平:

let seats: Option<Vec<_>> = data
    .lines()
    .map(|l| l.ok())
    .map(|l| l.map(replace))
    .map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).ok()))
    // if you want to keep chaining
    .map(|l| l.and_then(|s| some_result_function(&s).ok()))
    .collect();

如果您只想跳过错误,则存在一个更优雅的解决方案filter_map

let seats: Vec<_> = data
    .lines()
    .filter_map(|l| l.ok())
    .map(replace)
    .filter_map(|l| u32::from_str_radix(&l, 2).ok())
    .collect();

如果要维护错误,则将错误装入 aBox<dyn Error>以考虑不同类型:

use std::error::Error;
// later in the code
let seats: Result<Vec<_>, Box<dyn Error>> = data
    .lines()
    .map(|x| x.map_err(|e| Box::new(e) as _))
    .map(|l| l.map(replace))
    .map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).map_err(|e| Box::new(e) as _)))
    .collect();

如果你不喜欢重复的.map_err(|e| Box::new(e) as _),那么为它做一个特征:

use std::error::Error;

trait BoxErr {
    type Boxed;
    fn box_err(self) -> Self::Boxed;
}

impl<T, E: Error + 'static> BoxErr for Result<T, E> {
    type Boxed = Result<T, Box<dyn Error>>;
    
    fn box_err(self) -> Self::Boxed {
        self.map_err(|x| Box::new(x) as Box<dyn Error>)
    }
}

// later in the code

let seats: Result<Vec<_>, Box<dyn Error>> = data
    .lines()
    .map(|x| x.box_err())
    .map(|l| l.map(replace))
    .map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).box_err()))
    .collect();


以上是在Rust中调用结果迭代器上的映射的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>