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}