如何迭代返回Rust中Futures的函数Vec?

时间:2018-06-14 05:38:42

标签: iterator rust future

是否可以遍历Vec,调用一个方法,在每个方法上返回Future,并构建一个Future链,由(最终)由消费者?是否执行后Future s取决于Future中早期Vec的结果。

澄清:

我正在处理一个可以从任意一组上游源获取数据的应用程序。

请求数据将依次检查每个来源。如果第一个来源有错误(Err),或者没有可用的数据(None),则会尝试第二个来源,依此类推。

每个来源应该只尝试一次,并且在所有来源返回结果之前不应尝试任何来源。记录错误,但忽略,将查询传递给下一个上游数据源。

我有一些工作代码用于获取元数据:

/// Attempts to read/write data to various external sources. These are
/// nested types, because a data source may exist as both a reader and a writer
struct StoreManager {
    /// Upstream data sources
    readers: Vec<Rc<RefCell<StoreRead>>>,
    /// Downstream data sinks
    writers: Vec<Rc<RefCell<StoreWrite>>>,
}

impl StoreRead for StoreManager {
    fn metadata(self: &Self, id: &Identifier) -> Box<Future<Option<Metadata>, Error>> {
       Box::new(ok(self.readers
            .iter()
            .map(|store| {
                executor::block_on(store.borrow().metadata(id)).unwrap_or_else(|err| {
                    error!("Error on metadata(): {:?}", err);
                    None
                })
            })
            .find(Option::is_some)
            .unwrap_or(None)))
    }
}

除了对所有BoxRc/RefCell废话的不满之外,我真正关心的是executor::block_on()电话。它会阻塞,等待每个Future返回结果,然后继续下一个。

鉴于可以调用fn_returning_future().or_else(|_| other_fn())等等,是否有可能建立这样的动态链?或者是否需要在移动到下一个迭代器之前对迭代器中的每个Future进行全面评估?

1 个答案:

答案 0 :(得分:3)

您可以使用stream::unfold将单个值转换为流。在这种情况下,我们可以使用filebeat.prospectors: - paths: - /var/log/nginx/virus123.log input_type: log fields: type:virus123 json.keys_under_root: true - paths: - /var/log/nginx/virus1234.log input_type: log fields: type:virus1234 json.keys_under_root: true setup.template.name: "filebeat-%{[beat.version]}" setup.template.pattern: "filebeat-%{[beat.version]}-*" setup.template.overwrite: true processors: - drop_fields: fields: ["beat","source"] output.elasticsearch: index: index: "filebeat-%{[beat.version]}-%{[fields.type]:other}-%{+yyyy.MM.dd}" hosts: ["http://127.0.0.1:9200"] 迭代器作为单个值。

IntoIter
extern crate futures; // 0.2.1

use futures::{executor, future, stream, Future, FutureExt, Stream, StreamExt};

type Error = ();

fn network_request(val: i32) -> impl Future<Item = i32, Error = Error> {        
    // Just for demonstration, don't do this in a real program
    use std::{thread, time::{Duration, Instant}};
    thread::sleep(Duration::from_secs(1));
    println!("Resolving {} at {:?}", val, Instant::now());

    future::ok(val * 100)
}

fn requests_in_sequence(vals: Vec<i32>) -> impl Stream<Item = i32, Error = Error> {
    stream::unfold(vals.into_iter(), |mut vals| {
        match vals.next() {
            Some(v) => network_request(v).map(|v| Some((v, vals))).left_future(),
            None => future::ok(None).right_future(),
        }
    })
}

fn main() {
    let s = requests_in_sequence(vec![1, 2, 3]);
    let f = s.for_each(|v| {
        println!("-> {}", v);
        Ok(())
    });
    executor::block_on(f).unwrap();
}

要忽略Resolving 1 at Instant { tv_sec: 3416957, tv_nsec: 29270595 } -> 100 Resolving 2 at Instant { tv_sec: 3416958, tv_nsec: 29450854 } -> 200 Resolving 3 at Instant { tv_sec: 3416959, tv_nsec: 29624479 } -> 300 Err,您必须将None移至Error,使Item输入Item

Result<Option<T>, Error>
extern crate futures; // 0.2.1

use futures::{executor, future, stream, Future, FutureExt, Never, Stream, StreamExt};

struct Error;

fn network_request(val: i32) -> impl Future<Item = Option<i32>, Error = Error> {
    // Just for demonstration, don't do this in a real program
    use std::{thread, time::{Duration, Instant}};
    thread::sleep(Duration::from_millis(100));
    println!("Resolving {} at {:?}", val, Instant::now());

    match val {
        1 => future::err(Error),          // An error
        2 => future::ok(None),            // No data
        _ => future::ok(Some(val * 100)), // Success
    }
}

fn requests_in_sequence<I>(vals: I) -> impl Stream<Item = Result<Option<i32>, Error>, Error = Never>
where
    I: IntoIterator<Item = i32>,
{
    stream::unfold(vals.into_iter(), |mut vals| {
        match vals.next() {
            Some(v) => {
                network_request(v)
                    .then(|v| future::ok(Some((v, vals)))) // Convert `Item` into `Result<Option<i32>, Error>`
                    .left_future()
            }
            None => future::ok(None).right_future(),
        }
    })
}

fn main() {
    let reqs = requests_in_sequence(vec![1, 2, 3, 4, 5]);

    let success = reqs
        .filter_map(|x| future::ok(x.ok())) // Ignore all `Result::Err`
        .filter_map(|x| future::ok(x))      // Ignore all `Option::None`
        .next();                            // Get first value

    match executor::block_on(success) {
        Ok((Some(v), _s)) => println!("First success: {}", v),
        Ok((None, _s)) => println!("No successful requests"),
        Err(_) => unreachable!("Impossible to fail"),
    }
}
  

是否可以建立像这样的动态链

是的,但它涉及大量的额外分配和间接,并且要求至少有一个值可以访问:

Resolving 1 at Instant { tv_sec: 3428278, tv_nsec: 513758474 }
Resolving 2 at Instant { tv_sec: 3428278, tv_nsec: 614059691 }
Resolving 3 at Instant { tv_sec: 3428278, tv_nsec: 714256066 }
First success: 300

另见:

  

是否需要在移动到下一个之前完全评估迭代器中的每个fn requests_in_sequence(vals: Vec<i32>) -> Box<Future<Item = i32, Error = Error>> { let mut vals = vals.into_iter(); let val1 = vals.next().expect("Need at least one value to start from"); vals.fold(Box::new(network_request(val1)), |acc, val| { Box::new(acc.or_else(move |_| network_request(val))) }) }

不是您自己的要求的一部分吗?强调我的:

  

请求数据将检查每个来源,依次。如果第一个来源出现错误(Future),或者没有可用数据(Err),则会尝试第二个来源