async_to_iter/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![no_std]
4
5extern crate alloc;
6
7use alloc::boxed::Box;
8use alloc::rc::Rc;
9use core::cell::Cell;
10use core::future::Future;
11use core::pin::Pin;
12use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
13
14/// Sink for iterator output values to be used in async code.
15///
16/// See [`make_iter()`] for a complete usage example.
17pub struct IterSink<T> {
18    inner: Rc<IterSinkInner<T>>,
19}
20
21impl<T> IterSink<T> {
22    fn new(inner: Rc<IterSinkInner<T>>) -> Self {
23        Self { inner }
24    }
25
26    /// Yield a value from the iterator.
27    ///
28    /// The returned future is meant to be awaited from async code.
29    ///
30    /// Usage example:
31    /// ```rust
32    /// # use async_to_iter::IterSink;
33    /// async fn my_iterator(sink: IterSink<u32>, some_flag: bool) {
34    ///     sink.yield_value(2).await;
35    ///     if some_flag {
36    ///         sink.yield_value(5).await;
37    ///     }
38    /// }
39    /// ```
40    ///
41    /// Note: this method should only be used in async code launched by [`make_iter()`].
42    /// It should not be used from async code running inside a third-party
43    /// or a custom async runtime (including `tokio` or `async-std`) — this can cause deadlocks,
44    /// panics and other kinds of incorrect behavior (although there will not be undefined
45    /// behavior).
46    pub fn yield_value(&self, value: T) -> impl Future<Output = ()> {
47        self.inner.set_value(value);
48        IterYield::new()
49    }
50}
51
52/// Internal data storage of the sink for iterator output values.
53///
54/// Stores a value yielded from async code until it is collected by [`IterAdapter`] and its
55/// [`Iterator`] implementation.
56struct IterSinkInner<T> {
57    value: Cell<Option<T>>,
58}
59
60impl<T> IterSinkInner<T> {
61    fn new() -> Self {
62        Self {
63            value: Cell::new(None),
64        }
65    }
66
67    fn set_value(&self, value: T) {
68        self.value.set(Some(value));
69    }
70
71    fn take_value(&self) -> Option<T> {
72        self.value.take()
73    }
74}
75
76/// Future returned from [`IterSink::yield_value`].
77///
78/// This future suspends (returns [`Poll::Pending`]) exactly once,
79/// and becomes ready when it is polled the next time.
80struct IterYield {
81    suspended: bool,
82}
83
84impl IterYield {
85    fn new() -> Self {
86        Self { suspended: false }
87    }
88}
89
90impl Future for IterYield {
91    type Output = ();
92
93    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
94        if self.suspended {
95            Poll::Ready(())
96        } else {
97            self.get_mut().suspended = true;
98            Poll::Pending
99        }
100    }
101}
102
103/// Make a no-op [`RawWaker`], like unstable [`core::task::Waker::noop()`] does.
104fn make_noop_raw_waker() -> RawWaker {
105    fn noop(_: *const ()) {}
106
107    fn noop_clone(_: *const ()) -> RawWaker {
108        make_noop_raw_waker()
109    }
110    static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
111    RawWaker::new(core::ptr::null(), &VTABLE)
112}
113
114/// Make a no-op [`Waker`], like unstable [`core::task::Waker::noop()`] does.
115fn make_noop_waker() -> Waker {
116    let raw_waker = make_noop_raw_waker();
117
118    // SAFETY: the no-op waker is unconditionally safe.
119    unsafe { Waker::from_raw(raw_waker) }
120}
121
122/// The iterator adapter that translates [`Iterator::next()`] into [`Future::poll()`] calls.
123struct IterAdapter<T, Fut> {
124    iter_sink_inner: Rc<IterSinkInner<T>>,
125    future: Pin<Box<Fut>>,
126}
127
128impl<T, Fut> IterAdapter<T, Fut> {
129    fn new<IntoFut>(into_future: IntoFut) -> Self
130    where
131        IntoFut: FnOnce(IterSink<T>) -> Fut,
132    {
133        let iter_sink_inner = Rc::new(IterSinkInner::new());
134        let iter_sink = IterSink::new(Rc::clone(&iter_sink_inner));
135        let future = Box::pin(into_future(iter_sink));
136        Self {
137            iter_sink_inner,
138            future,
139        }
140    }
141}
142
143impl<T, Fut> Iterator for IterAdapter<T, Fut>
144where
145    Fut: Future<Output = ()>,
146{
147    type Item = T;
148
149    fn next(&mut self) -> Option<Self::Item> {
150        let waker = make_noop_waker();
151        let mut cx = Context::from_waker(&waker);
152        match self.future.as_mut().poll(&mut cx) {
153            Poll::Pending => Some(
154                self.iter_sink_inner
155                    .take_value()
156                    .expect("Future did not yield a value"),
157            ),
158            Poll::Ready(()) => None,
159        }
160    }
161}
162
163/// Create an iterator implemented by async code.
164///
165/// Async code that provides the iterator implementation is accepted via the `into_future`
166/// parameter, which is a function that accepts an [`IterSink`] and returns a future, usually one
167/// returned by an async function. This can be used to turn an async function to a generator on
168/// stable Rust.
169///
170/// The async code can use [`IterSink::yield_value()`] to yield values from the iterator. Each
171/// yielded value will be returned from one call to [`Iterator::next()`]. Async code should only
172/// await on futures returned by [`IterSink`] (or transitively on futures that do so), otherwise
173/// the behavior may be incorrect, including deadlocks or panics.
174///
175/// Usage example:
176///
177/// ```
178/// # use async_to_iter::{IterSink, make_iter};
179/// async fn count_to_impl(sink: IterSink<u32>, n: u32) {
180///     for i in 1..=n {
181///         sink.yield_value(i).await;
182///     }
183/// }
184///
185/// fn count_to(n: u32) -> impl Iterator<Item = u32> {
186///     make_iter(move |sink| count_to_impl(sink, n))
187/// }
188///
189/// let mut iter = count_to(3);
190/// assert_eq!(iter.next(), Some(1));
191/// assert_eq!(iter.next(), Some(2));
192/// assert_eq!(iter.next(), Some(3));
193/// assert_eq!(iter.next(), None);
194/// ```
195pub fn make_iter<T, Fut, IntoFut>(into_future: IntoFut) -> impl Iterator<Item = T>
196where
197    Fut: Future<Output = ()>,
198    IntoFut: FnOnce(IterSink<T>) -> Fut,
199{
200    IterAdapter::new(into_future)
201}
202
203#[cfg(test)]
204mod tests {
205    use super::{make_iter, IterSink};
206    use alloc::string::String;
207    use alloc::vec::Vec;
208
209    async fn count_up_impl(sink: IterSink<u64>, start: u64) {
210        for i in start.. {
211            sink.yield_value(i).await;
212        }
213    }
214
215    fn count_up(start: u64) -> impl Iterator<Item = u64> {
216        make_iter(move |sink| count_up_impl(sink, start))
217    }
218
219    #[test]
220    fn test_count_up() {
221        let mut iter = count_up(5);
222        assert_eq!(iter.next(), Some(5));
223        assert_eq!(iter.next(), Some(6));
224        assert_eq!(iter.next(), Some(7));
225        assert_eq!(iter.next(), Some(8));
226        assert_eq!(iter.next(), Some(9));
227        assert_eq!(iter.next(), Some(10));
228        assert_eq!(iter.next(), Some(11));
229        assert_eq!(iter.next(), Some(12));
230    }
231
232    async fn join_to_strings_impl(sink: IterSink<String>, chars: impl IntoIterator<Item = char>) {
233        let mut is_whitespace = true;
234        let mut current_string = String::new();
235        for c in chars {
236            match (c.is_whitespace(), is_whitespace) {
237                (true, true) => (),
238                (true, false) => {
239                    sink.yield_value(core::mem::take(&mut current_string)).await;
240                    is_whitespace = true;
241                }
242                (false, _) => {
243                    current_string.push(c);
244                    is_whitespace = false;
245                }
246            }
247        }
248
249        if !is_whitespace {
250            sink.yield_value(current_string).await;
251        }
252    }
253
254    fn join_to_strings(chars: impl IntoIterator<Item = char>) -> impl Iterator<Item = String> {
255        make_iter(move |sink| join_to_strings_impl(sink, chars))
256    }
257
258    #[test]
259    fn test_join_to_strings() {
260        let input1 = "Hello   world!";
261        let output1 = join_to_strings(input1.chars()).collect::<Vec<_>>();
262        assert_eq!(&output1, &[String::from("Hello"), String::from("world!")],);
263
264        let input2 = "    test\ton more\n  data ";
265        let output2 = join_to_strings(input2.chars()).collect::<Vec<_>>();
266        assert_eq!(
267            &output2,
268            &[
269                String::from("test"),
270                String::from("on"),
271                String::from("more"),
272                String::from("data"),
273            ],
274        );
275    }
276}