当前位置: 首页 > 知识库问答 >
问题:

如何停止迭代并返回一个错误时,迭代器::映射返回结果::Err?

韶亮
2023-03-14

我有一个返回结果的函数

fn find(id: &Id) -> Result<Item, ItemError> {
    // ...
}

然后另一个用户这样使用它:

let parent_items: Vec<Item> = parent_ids.iter()
    .map(|id| find(id).unwrap())
    .collect();

如何处理任何map迭代中的失败情况?

我知道我可以使用flat_map,在这种情况下,错误结果将被忽略:

let parent_items: Vec<Item> = parent_ids.iter()
    .flat_map(|id| find(id).into_iter())
    .collect();

Result的迭代器根据成功状态具有0或1项,如果为0,flat_map将过滤掉它。

但是,我不想忽略错误,而是想让整个代码块停止并返回一个新错误(基于映射中出现的错误,或者只是转发现有错误)。

在Rust中如何最好地处理此问题?

共有3个答案

颛孙昆
2023-03-14

这个答案与Rust的1.0之前版本有关,所需的功能已被删除

您可以使用std::result::fold函数进行此操作。遇到第一个Err后,它停止迭代。

我刚刚编写的一个示例程序:

fn main() {
  println!("{}", go([1, 2, 3]));
  println!("{}", go([1, -2, 3]));
}

fn go(v: &[int]) -> Result<Vec<int>, String> {
    std::result::fold(
        v.iter().map(|&n| is_positive(n)),
        vec![],
        |mut v, e| {
            v.push(e);
            v
        })
}

fn is_positive(n: int) -> Result<int, String> {
    if n > 0 {
        Ok(n)
    } else {
        Err(format!("{} is not positive!", n))
    }
}

输出:

Ok([1, 2, 3])
Err(-2 is not positive!)

演示

司宏伯
2023-03-14
匿名用户

接受的答案显示了如何在收集时停止出错,这很好,因为这是OP要求的。如果您需要在大型或无限容易出错的迭代器上进行处理,请继续阅读。

如前所述,for可以用来模拟出错时停止,但这有时并不雅观,比如当您想调用max()或其他消费方法时。在其他情况下,这几乎是不可能的,比如消费方法在另一个板条箱中,比如itertools或人造丝1

当您控制迭代器的使用方式时,只需使用try_for_each在出现第一个错误时停止。它接受一个返回结果的闭包,如果闭包每次都返回Ok,并且第一个错误返回Err,则它将返回Ok(())。这使得闭包只需使用 运算符:

use std::{fs, io};

fn main() -> io::Result<()> {
    fs::read_dir("/")?.try_for_each(|e| -> io::Result<()> {
        println!("{}", e?.path().display());
        Ok(())
    })?;
    // ...
    Ok(())
}

如果需要在调用闭包之间保持状态,也可以使用try\u fold。这两种方法都是由并行迭代器实现的,所以同样的模式也适用于人造丝。

这种方法要求您控制迭代器的使用方式。如果这是由不受您控制的代码完成的——例如,如果您将迭代器传递给itertools::mer()或类似的,您将需要一个适配器。

出现错误时停止的第一次尝试是使用take_while

use std::{io, fs};

fn main() -> io::Result<()> {
    fs::read_dir("/")?
        .take_while(Result::is_ok)
        .map(Result::unwrap)
        .for_each(|e| println!("{}", e.path().display()));
    // ...
    Ok(())
}

这是可行的,但我们没有得到任何错误发生的迹象,迭代只是静静地停止。此外,它还需要难看的映射(Result::unwrap),这使得程序似乎会在出错时死机,而事实上,当我们在出错时停止时,情况并非如此。

这两个问题都可以通过从take_while切换到扫描来修复,这是一个更强大的组合器,不仅支持停止迭代,还传递其回调拥有的项目,允许闭包将错误提取给调用者:

fn main() -> io::Result<()> {
    let mut err = Ok(());
    fs::read_dir("/")?
        .scan(&mut err, |err, res| match res {
            Ok(o) => Some(o),
            Err(e) => {
                **err = Err(e);
                None
            }
        })
        .for_each(|e| println!("{}", e.path().display()));
    err?;
    // ...
    Ok(())
}

如果在多个地方需要,可以将闭包抽象为一个实用函数:

fn until_err<T, E>(err: &mut &mut Result<(), E>, item: Result<T, E>) -> Option<T> {
    match item {
        Ok(item) => Some(item),
        Err(e) => {
            **err = Err(e);
            None
        }
    }
}

...在这种情况下,我们可以调用它作为。扫描(

这些示例使用for_each()简单地耗尽迭代器,但可以通过任意操作将其链接,包括Rayon的par_bridge()。使用scan()甚至可以collect()将项目收集到一个容器中,并访问错误之前看到的项目,这在收集到结果时有时很有用,但不可用

1在使用人造丝并行处理流数据时,需要使用par_bridge()出现:

fn process(input: impl BufRead + Send) -> std::Result<Output, Error> {
    let mut err = Ok(());
    let output = lines
        .input()
        .scan(&mut err, until_err)
        .par_bridge()
        .map(|line| ... executed in parallel ... )
        .reduce(|item| ... also executed in parallel ...);
    err?;
    ...
    Ok(output)
}

同样,通过收集到结果中,也无法轻松实现等效效果。

武嘉祥
2023-03-14

Result实现了迭代器的功能,因此您可以将结果移到外部,迭代器将处理其余部分(包括在发现错误时停止迭代)。

#[derive(Debug)]
struct Item;
type Id = String;

fn find(id: &Id) -> Result<Item, String> {
    Err(format!("Not found: {:?}", id))
}

fn main() {
    let s = |s: &str| s.to_string();
    let ids = vec![s("1"), s("2"), s("3")];

    let items: Result<Vec<_>, _> = ids.iter().map(find).collect();
    println!("Result: {:?}", items);
}

游乐场

 类似资料:
  • 在spark中使用mapPartitionsToPair/PairFlatMapFunction时,我在Internet上找到了一个类似的例子 但当康普利 我找到了的声明 所以调用应该是返回一个迭代器。 因此,有人能帮助我如何返回在javaRDD api火花迭代器?谢谢 PS:我试过下面这样的代码,但在集群上不起作用:

  • 我正在做一些产生正确结果的事情。然而,从设计观点来看,这是错误的。 该程序的重点是列出一个数字的所有幂的结果,直到并包括用户定义的限制。 我有一个构造函数,它接受扫描器中的基和指数。然后是一种利用for循环计算每个指数的幂的方法。 现在,问题是我直接从这个方法打印每个循环迭代的结果。这超过了私有变量的点,它首先是无效的。 因此,我想定义一个getter方法,它将每个幂的结果返回给输出。我过去常常为

  • 我有个智囊团。我需要迭代并返回一个值! 我能和Lambda一起做这件事吗?怎么做?(如何获取具有类型_12项的组的id?类型_12项将在所有组中仅为一个) 提前致谢这是我的代码:

  • 将这些视为对象: 查看java文档,对于LinkedList类,LinkedList类中没有迭代器方法的实现,但是,实现是在AbstractSequentialList类中。 listIterator()方法在AbstractList类中实现,AbstractSequentialList的父类,总结一下,如果我没弄错的话,它返回一个不使用节点概念的迭代器对象。 但是方法是在LinkedList类中

  • 我可以看到返回。但是现在已经添加到C++20标准中,为什么返回?cppreference指定: 返回值 等于last的迭代器。 这个选择背后的理性是什么? 与相比,用例的优势是什么?

  • 本文向大家介绍C++映射迭代器,包括了C++映射迭代器的使用技巧和注意事项,需要的朋友参考一下 示例 容器中第一个元素的迭代器。 如果映射对象是const限定符,则该函数返回const_iterator。否则,返回iterator。 输出: a => 200 b => 100 c => 300