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
use crateCoro;
use crateFixedPointCoro;
use crateSuspend;
type RecursiveFn<'a, I, Y, R> =
dyn Fn ;
;
/// Creates a coroutine using a recursive function.
///
/// The function takes a wrapper of a reference to itself alongside the input
/// type to the coroutine, and returns either `Yield` with a yielded value and
/// the recursive wrapper, or `Return` with the return value.
///
/// The function must be `'static` because it is referenced as a trait object
/// to get around the limitations of
/// <https://github.com/rust-lang/rust/issues/97680>. Rust does not allow
/// closure objects to take themselves, or types that own references to
/// themselves, as inputs. However, it is possible for a closure to take a
/// reference to a `dyn Fn` object that happens to be itself.
///
/// This is also why `f` is passed by reference: the wrapped closure object must
/// not own the `Fn` because it must pass "itself" (actually a copy that wraps
/// the same reference to the `Fn`) to the function, and if the wrapper were to
/// own the `Fn`, then it would be impossible to invoke the `Fn` with the value
/// after the wrapper had been constructed.
///
/// Whereas `yield_with()` is useful for infinite coroutines that never return,
/// and `from_fn()` is useful for guaranteed-finite coroutines that must return
/// within a knowable finite number of steps, `recursive()` is useful for
/// creating coroutines that may return, but without a finite bound known at
/// compile time.
///
/// The result of `recursive()` is a `FixedPointCoro`, which means that its
/// `Next` type is itself. This allows the coroutine to be looped over
/// iteratively without recursion, by assigning the result of `resume()` back to
/// the coroutine variable when the `Suspend` result matches `Yield`. It also
/// enables methods like `into_iter()` when the input type `I` implements
/// `Default`.
///
/// # Example
///
/// ```rust
/// use cocoro::Coro;
/// use cocoro::Return;
/// use cocoro::Void;
/// use cocoro::Yield;
/// use cocoro::recursive;
///
/// recursive(&|recur, n| Yield(n + 1, recur))
/// .returns::<Void>()
/// .assert_yields(1, 0)
/// .assert_yields(2, 1)
/// .assert_yields(3, 2);
/// ```
+ 'a
where
Y: 'a,
R: 'a,
I: 'a,
F: Fn + 'static,