futures_util/future/
loop_fn.rs

1//! Definition of the `LoopFn` combinator, implementing `Future` loops.
2
3use futures_core::{Async, Future, IntoFuture, Poll};
4use futures_core::task;
5
6/// An enum describing whether to `break` or `continue` a `loop_fn` loop.
7#[derive(Debug)]
8pub enum Loop<T, S> {
9    /// Indicates that the loop has completed with output `T`.
10    Break(T),
11
12    /// Indicates that the loop function should be called again with input
13    /// state `S`.
14    Continue(S),
15}
16
17/// A future implementing a tail-recursive loop.
18///
19/// Created by the `loop_fn` function.
20#[must_use = "futures do nothing unless polled"]
21#[derive(Debug)]
22pub struct LoopFn<A, F> where A: IntoFuture {
23    future: A::Future,
24    func: F,
25}
26
27/// Creates a new future implementing a tail-recursive loop.
28///
29/// The loop function is immediately called with `initial_state` and should
30/// return a value that can be converted to a future. On successful completion,
31/// this future should output a `Loop<T, S>` to indicate the status of the
32/// loop.
33///
34/// `Loop::Break(T)` halts the loop and completes the future with output `T`.
35///
36/// `Loop::Continue(S)` reinvokes the loop function with state `S`. The returned
37/// future will be subsequently polled for a new `Loop<T, S>` value.
38///
39/// # Examples
40///
41/// ```
42/// # extern crate futures;
43/// use futures::prelude::*;
44/// use futures::future::{self, ok, loop_fn, Loop, FutureResult};
45/// use futures::never::Never;
46///
47/// struct Client {
48///     ping_count: u8,
49/// }
50///
51/// impl Client {
52///     fn new() -> Self {
53///         Client { ping_count: 0 }
54///     }
55///
56///     fn send_ping(self) -> FutureResult<Self, Never> {
57///         ok(Client { ping_count: self.ping_count + 1 })
58///     }
59///
60///     fn receive_pong(self) -> FutureResult<(Self, bool), Never> {
61///         let done = self.ping_count >= 5;
62///         ok((self, done))
63///     }
64/// }
65///
66/// # fn main() {
67/// let ping_til_done = loop_fn(Client::new(), |client| {
68///     client.send_ping()
69///         .and_then(|client| client.receive_pong())
70///         .and_then(|(client, done)| {
71///             if done {
72///                 Ok(Loop::Break(client))
73///             } else {
74///                 Ok(Loop::Continue(client))
75///             }
76///         })
77/// });
78/// # }
79/// ```
80pub fn loop_fn<S, T, A, F>(initial_state: S, mut func: F) -> LoopFn<A, F>
81    where F: FnMut(S) -> A,
82          A: IntoFuture<Item = Loop<T, S>>,
83{
84    LoopFn {
85        future: func(initial_state).into_future(),
86        func: func,
87    }
88}
89
90impl<S, T, A, F> Future for LoopFn<A, F>
91    where F: FnMut(S) -> A,
92          A: IntoFuture<Item = Loop<T, S>>,
93{
94    type Item = T;
95    type Error = A::Error;
96
97    fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
98        loop {
99            match try_ready!(self.future.poll(cx)) {
100                Loop::Break(x) => return Ok(Async::Ready(x)),
101                Loop::Continue(s) => self.future = (self.func)(s).into_future(),
102            }
103        }
104    }
105}