1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crateCoro;
use crateSuspend;
use crateReturn;
use crateYield;
use crateSuspended;
/// Creates a coroutine that carries state through its execution.
///
/// Usually, if you want a stateful coroutine, you can reach for other helper
/// functions like `yield_with()` or `from_control_flow()`. However, there are
/// situations where you need a `FixedPointCoro` that returns its state, without
/// copying it, in which case you'll find that `from_control_flow()` will be
/// unable to move the state out of its closure because the closure has to
/// implement `FnMut`, and `yield_with()` is not suitable for that and because
/// it never returns.
///
/// The `with_state()` helper lets you feed some initial state into a coroutine
/// that must accept the state as part of its input and return the state back
/// when it yields. The resulting coroutine encapsulates the act of passing the
/// from the initial state to the coroutine's input and back from the yield
/// values to the next input, making the final shape of the coroutine look like
/// `Coro<I, Y, R>`.
///
/// This is particularly useful when the input coroutine is a `FixedPointCoro`,
/// such as one implemented by `recursive()`, which is otherwise unable to
/// capture state because of `'static` restrictions and the requirement that
/// the closure implements `Fn`, rather than `FnOnce` or `FnMut`. If you use
/// `recursive()` to make a `Coro<(S, I), (S, Y), R>`, you can then use
/// `with_state()` to create a `Coro<I, Y, R>` that carries the state through
/// its execution without copying it, getting the best of both worlds.
///
/// # Example
///
/// Suppose you want to create a coroutine that fills up a buffer with inputs
/// until it reaches a specified size, and then returns the buffer.
///
/// Naively, you might try to use `from_control_flow()`, but you'll run into a
/// problem because the closure passed to `from_control_flow()` cannot move
/// the `Vec` back out of its closure:
///
/// ```compile_fail
/// use cocoro::{Coro, from_control_flow};
/// use core::ops::ControlFlow::{Continue, Break};
///
/// // Fill up a buffer with the coroutine inputs until it reaches a specified
/// // size, then return the buffer.
/// fn fill(n: usize) -> impl Coro<i32, (), Vec<i32>> {
/// let mut buffer = Vec::new();
/// from_control_flow(move |input: i32| {
/// if buffer.len() < n {
/// buffer.push(input);
/// Continue(())
/// } else {
/// // ERROR: cannot move out of `buffer`, a captured variable in an
/// // `FnMut` closure
/// Break(buffer)
/// }
/// })
/// }
/// ```
///
/// Instead of capturing the `Vec` in the closure, you can instead pass the
/// `Vec` in and out of the coroutine through the input and yield values, and
/// let `with_state()` handle passing the state through the coroutine:
///
/// ```rust
/// use core::ops::ControlFlow::Break;
/// use core::ops::ControlFlow::Continue;
///
/// use cocoro::Coro;
/// use cocoro::from_control_flow;
/// use cocoro::with_state;
///
/// // Fill up a buffer with the coroutine inputs until it reaches a specified
/// // size, then return the buffer.
/// fn fill(n: usize) -> impl Coro<i32, (), Vec<i32>> {
/// let buffer = Vec::new();
/// with_state(
/// (buffer, n),
/// from_control_flow(
/// move |((mut buffer, n), input): ((Vec<_>, _), _)| {
/// if buffer.len() < n {
/// buffer.push(input);
/// Continue(((buffer, n), ()))
/// } else {
/// // OK: `buffer` is owned locally, rather than by the
/// // closure, so it can be returned.
/// Break(buffer)
/// }
/// },
/// ),
/// )
/// }
/// ```