我正在寻找一种方法来消除此示例中的临时向量分配:
fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
println!(
"{}: {:?}",
name,
iter.collect::<Result<Vec<_>, _>>()
.map(|v| v.into_iter().min())
);
}
fn main() {
let without_errors = vec![Ok(1), Ok(2), Ok(3)];
let with_errors = vec![Ok(1), Err("error"), Ok(2)];
doit("without errors", without_errors.into_iter());
doit("with errors", with_errors.into_iter());
}
这是带有错误处理主题的迭代器的变体,除了我不想创建集合(因此collect()
不能完成这项工作),但我想对正在迭代的元素执行进一步的操作。
请注意,这会给出错误的结果,因为Ok
小于Err
:
fn doit<T: Iterator<Item = Result<i32, &'static str>>>(name: &str, iter: T) {
println!("{}: {:?}", name, iter.min());
}
它会偶然给出max()
的正确结果,但它不会停止迭代第一个错误。
Iterator::try_fold
为您提供所需的框架,自Rust 1.27(Playground)以来可用:
fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
I: Iterator<Item = Result<T, E>>,
T: Ord,
F: Fn(T, T) -> T,
{
iter.try_fold(None, |r, i| {
let i = i?;
Ok(Some(if let Some(r) = r { f(r, i) } else { i }))
})
}
fn main() {
let without_errors = vec![Ok(1), Ok(2), Ok(3)];
let with_errors = vec![Ok(1), Err("error"), Ok(2)];
fn doit<'r, T>(name: &str, iter: T)
where
T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
{
println!("{}: {:?}", name, fold_ok(iter.cloned(), ::std::cmp::min));
}
doit("without errors", without_errors.iter());
doit("with errors", with_errors.iter());
}
在此之前,我认为您唯一的选择是手动迭代(Playground)
fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
I: Iterator<Item = Result<T, E>>,
T: Ord,
F: Fn(T, T) -> T,
{
let mut result = match iter.next() {
None => return Ok(None),
Some(r) => r?,
};
for item in iter {
result = f(result, item?);
}
Ok(Some(result))
}
fn main() {
let without_errors = vec![Ok(1), Ok(2), Ok(3)];
let with_errors = vec![Ok(1), Err("error"), Ok(2)];
fn doit<'r, T>(name: &str, iter: T)
where
T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
{
println!(
"{}: {:?}",
name,
fold_ok(iter.clone().cloned(), ::std::cmp::min)
);
}
doit("without errors", without_errors.iter());
doit("with errors", with_errors.iter());
}
“提升”一个函数来处理结果的迭代器是一个相当常见的模式,像往常一样,itertools有一个解决方案 - process_results
:
use itertools; // 0.8.0
fn doit(name: &str, iter: impl Iterator<Item = Result<i32, &'static str>>) {
let min = itertools::process_results(iter, |i| i.min());
println!("{}: {:?}", name, min);
}
这段代码在被提取到itertools之前就像ResultShunt
in the standard library一样开始了。它是sum
和product
对Result
迭代器的实现的基础。
有可能滥用collect()
:
pub struct Min<T> {
value: Option<T>,
}
impl<T> Min<T> {
pub fn value(self) -> Option<T> {
self.value
}
}
impl<T> std::iter::FromIterator<T> for Min<T>
where
T: Ord,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut iter = iter.into_iter();
match iter.next() {
None => Min { value: None },
Some(mut value) => {
for i in iter {
value = std::cmp::min(value, i);
}
Min { value: Some(value) }
}
}
}
}
这可以通过iter.collect::<Min<_>>().value()
使用。这是很多机器,我没有看到一种方法来抽象它(所以你只需要提供std::cmp::min
或其他一些半群操作)。
我没有朝着Iterator::try_fold
的方向看,它提供了大部分机器。