【问题标题】:Calling map on Iter of Results in Rust在 Rust 结果的 Iter 上调用 map
【发布时间】:2020-12-05 07:38:35
【问题描述】:

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

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

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

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

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

最小的工作示例

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

use eyre::Context;

fn main() {
    let data = std::io::Cursor::new(b"FFBFFFBLLL\nBFBFBBFRLR\nFFFBFFBLLL");

    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")
}

更多参考资料:

【问题讨论】:

    标签: rust rust-result


    【解决方案1】:

    由于您无论如何都丢弃了错误类型,因此您可以完全避免eyre,并使用.okResult 转换为Option,然后只需使用Optionand_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();
    

    如果您想维护错误,请将错误放入 Box&lt;dyn Error&gt; 以解决不同的类型:

    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 _),那就给它做一个trait:

    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();
    

    【讨论】:

    • 感谢您的回答。有没有办法像你一样优雅地做到这一点,而且还能检测到错误?
    猜你喜欢
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    • 2021-06-28
    • 2017-05-25
    • 2022-01-13
    • 1970-01-01
    相关资源
    最近更新 更多