How do I stop iteration and return an error when Iterator::map returns a Result::Err?
我有一个返回
1 2 3 | fn find(id: &Id) -> Result<Item, ItemError> { // ... } |
然后再像这样使用它:
1 2 3 | let parent_items: Vec<Item> = parent_ids.iter() .map(|id| find(id).unwrap()) .collect(); |
如何处理任何
我知道我可以使用
1 2 3 | let parent_items: Vec<Item> = parent_ids.iter() .flat_map(|id| find(id).into_iter()) .collect(); |
但是,我不想忽略错误,而是想使整个代码块停止并返回一个新错误(基于映射中出现的错误,或者仅转发现有错误)。
如何最好地在Rust中处理此问题?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #[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); } |
游乐场
可接受的答案显示了如何在收集时停止错误,这很好,因为这是OP所要求的。如果您需要同样适用于大型或无限易错迭代器的处理,请继续阅读。
如前所述,
。
迭代器使用者:
控制迭代器的使用方式时,只需使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | use std::{io, fs}; fn main() -> io::Result<()> { fs::read_dir("/")? .take_while(Result::is_ok) .map(Result::unwrap) .try_for_each(|e| -> io::Result<()> { println!("{}", e.path().display()); Ok(()) })?; // ... Ok(()) } |
如果需要在闭包调用之间保持状态,则也可以使用
此方法要求您控制迭代器的使用方式。如果这是通过不受您控制的代码完成的-例如,如果要将迭代器传递给
迭代器适配器:
在出错时停止的第一个尝试是使用
1 2 3 4 5 6 7 8 9 10 | 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(()) } |
这可行,但是我们没有任何迹象表明发生了错误,迭代只是默默地停止了。此外,它还需要难看的
可以通过将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(()) } |
如果需要在多个地方使用,则可以将闭包抽象为实用函数:
1 2 3 4 5 6 7 8 9 | 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 } } } |
...在这种情况下,我们可以将其作为
这些示例用
1使用Rayon并行处理流数据时,需要使用`par_bridge()`:
1 2 3 4 5 6 7 8 9 10 11 12 | 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) } |
同样,通过收集到
This answer pertains to a pre-1.0 version of Rust and the required functions were removed
您可以为此使用
后,它将停止迭代。
我刚刚编写的示例程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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)) } } |
输出:
1 2 | Ok([1, 2, 3]) Err(-2 is not positive!) |
演示