在 Rust 的结果 Iter 上调用映射
Calling map on Iter of Results in Rust
我想以“函数式编程”风格编写一些代码。
但是,我从一个结果迭代器开始,我只想将该函数应用于 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")
}
更多参考资料:
- How do I stop iteration and return an error when Iterator::map returns a 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(我正在使用 rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24))
lines()
returns Result
s: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
既然你放弃了错误类型,你可以完全避免 eyre
并使用 .ok
将 Result
转换为 Option
,然后只需使用 Option
的 and_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<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();
我想以“函数式编程”风格编写一些代码。
但是,我从一个结果迭代器开始,我只想将该函数应用于 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")
}
更多参考资料:
- How do I stop iteration and return an error when Iterator::map returns a 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(我正在使用 rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24))lines()
returnsResult
s: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
既然你放弃了错误类型,你可以完全避免 eyre
并使用 .ok
将 Result
转换为 Option
,然后只需使用 Option
的 and_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<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();