处理流时如何删除或忽略错误?

时间:2019-02-08 14:15:28

标签: rust future

我有一长串希望使用Stream::buffer_unordered / Stream::buffered运行的期货。我将此流与for_each合并为一个单独的future,然后使用Tokio对其全部执行。期货之一返回错误是很常见的。根据文档,for_each将在返回错误时停止。

返回这些错误后如何忽略或仅打印一条消息,并继续执行后续期货?

以下是与我的情况类似的通用代码:

use futures::stream;
use futures::stream::Stream;
use futures::future::err;
use futures::future::ok;
use tokio;

fn main() {
    let queries: Vec<u32> = (0..10).collect();
    let futures = queries.into_iter().map(move |num| {
        println!("Started {}", num);
        // Maybe throw error
        let future = match num % 3 {
            0 => ok::<u32, u32>(num),
            _ => err::<u32, u32>(num)
        };
        future
    });

    let stream = stream::iter_ok(futures);
    let num_workers = 8;
    let future = stream
        .buffer_unordered(num_workers)
        .map_err(|err| {
            println!("Error on {:?}", err);
        })
        .for_each(|n| {
            println!("Success on {:?}", n);
            Ok(())
        });

    tokio::runtime::run(future);
}

Rust Playground

如果您尝试此示例,则抛出Err时,期货队列将尽早停止执行。

1 个答案:

答案 0 :(得分:2)

  • Stream::map_err-提供错误值,它可以转换类型,但将其保留为错误。

  • Stream::or_else-带有错误值,它可以将错误转换为成功,而使成功值保持不变。

  • Stream::then-提供成功值和错误值,并且可以做您想做的任何事情。

Stream::map不能让您将错误转化为成功,所以它没有用。 Stream::or_else 确实提供功能,但是当您可以将错误类型转换为成功类型时使用。只有Stream::then使您能够一次转换两种类型。

Stream::flatten可用于将一个流转换为单个流。

使用Result可被视为迭代器的事实将其组合起来,您可以创建以下代码:

stream
    .then(|r| future::ok(stream::iter_ok::<_, ()>(r)))
    .flatten()

无论流的项目是Ok还是Err,我们都将其转换为迭代器并从中创建流。然后,我们将数据流弄平。

如果您想打印出错误,我会使用Stream::inspect_err

stream.inspect_err(|err| println!("Error on {:?}", err))

完整代码:

use futures::{
    future,
    stream::{self, Stream},
}; // 0.1.25;
use tokio; // 0.1.14

fn main() {
    let stream = stream::iter_ok({
        (0..10).map(|num| {
            println!("Started {}", num);
            match num % 3 {
                0 => future::ok(num),
                _ => future::err(num),
            }
        })
    })
    .buffer_unordered(2);

    let stream = stream
        .inspect_err(|err| println!("Error on {:?}", err))
        .then(|r| future::ok(stream::iter_ok::<_, ()>(r)))
        .flatten();

    tokio::run({
        stream.for_each(|n| {
            println!("Success on {:?}", n);
            Ok(())
        })
    });
}
Started 0
Started 1
Success on 0
Started 2
Error on 1
Started 3
Error on 2
Started 4
Success on 3
Started 5
Error on 4
Started 6
Error on 5
Started 7
Success on 6
Started 8
Error on 7
Started 9
Error on 8
Success on 9