try_next/
lib.rs

1//! Minimal traits for synchronous, fallible, pull-based item sources.
2//!
3//! This module defines two related traits:
4//!
5//! - [`TryNext`] — a context-free, fallible producer of items,
6//! - [`TryNextWithContext`] — a context-aware variant that allows the caller
7//!   to supply mutable external state on each iteration step.
8//!
9//! Both traits follow the same basic pattern: they represent a source that can
10//! **attempt to produce the next item**, which may succeed, fail, or signal the
11//! end of the sequence.
12//!
13//! ## Core idea
14//!
15//! Each `try_next*` method call returns a [`Result`] with three possible outcomes:
16//!
17//! * `Ok(Some(item))` — a successfully produced item,
18//! * `Ok(None)` — no more items are available (the source is exhausted),
19//! * `Err(error)` — an error occurred while trying to produce the next item.
20//!
21//! These traits are **synchronous** — each call blocks until a result is ready.
22//! They are suitable for ordinary blocking or CPU-bound producers such as parsers,
23//! generators, or readers. For asynchronous, non-blocking sources, use
24//! [`futures::TryStream`](https://docs.rs/futures/latest/futures/stream/trait.TryStream.html).
25//!
26//! ## [`TryNext`]
27//!
28//! The simplest case: a self-contained, fallible producer that doesn’t depend on
29//! any external context.
30//!
31//! ```rust
32//! use try_next::TryNext;
33//!
34//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
35//! enum MyError { Broken }
36//!
37//! struct Demo { state: u8 }
38//!
39//! impl TryNext for Demo {
40//!     type Item = u8;
41//!     type Error = MyError;
42//!
43//!     fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
44//!         match self.state {
45//!             0..=2 => {
46//!                 let v = self.state;
47//!                 self.state += 1;
48//!                 Ok(Some(v))
49//!             }
50//!             3 => {
51//!                 self.state += 1;
52//!                 Ok(None)
53//!             }
54//!             _ => Err(MyError::Broken),
55//!         }
56//!     }
57//! }
58//!
59//! let mut src = Demo { state: 0 };
60//! assert_eq!(src.try_next(), Ok(Some(0)));
61//! assert_eq!(src.try_next(), Ok(Some(1)));
62//! assert_eq!(src.try_next(), Ok(Some(2)));
63//! assert_eq!(src.try_next(), Ok(None));
64//! assert_eq!(src.try_next(), Err(MyError::Broken));
65//! ```
66//!
67//! ## [`TryNextWithContext`]
68//!
69//! A generalization of [`TryNext`] that allows each call to [`try_next_with_context`]
70//! to receive a mutable reference to a caller-supplied **context**.
71//!
72//! The context can hold shared mutable state, configuration data, or external
73//! resources such as file handles, buffers, or clients. This pattern is useful when
74//! the producer needs external help or coordination to produce the next item, while
75//! keeping the trait itself simple and generic.
76//!
77//! ```rust
78//! use try_next::TryNextWithContext;
79//!
80//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
81//! enum MyError { Fail }
82//!
83//! struct Producer;
84//!
85//! struct Ctx { next_val: u8 }
86//!
87//! impl TryNextWithContext for Producer {
88//!     type Item = u8;
89//!     type Error = MyError;
90//!     type Context = Ctx;
91//!
92//!     fn try_next_with_context(
93//!         &mut self,
94//!         ctx: &mut Self::Context,
95//!     ) -> Result<Option<Self::Item>, Self::Error> {
96//!         if ctx.next_val < 3 {
97//!             let v = ctx.next_val;
98//!             ctx.next_val += 1;
99//!             Ok(Some(v))
100//!         } else {
101//!             Ok(None)
102//!         }
103//!     }
104//! }
105//!
106//! let mut producer = Producer;
107//! let mut ctx = Ctx { next_val: 0 };
108//!
109//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(0)));
110//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(1)));
111//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(2)));
112//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(None));
113//! ```
114//!
115//! ## Design notes
116//!
117//! - Both traits are deliberately **minimal**: they define no combinators or adapters.
118//!   Their purpose is to provide a simple, low-level interface for fallible, stepwise
119//!   data production.
120//! - `TryNextWithContext` can often serve as a building block for adapters that
121//!   integrate external state or resources.
122//! - These traits are a good fit for *incremental* or *stateful* producers such as
123//!   **parsers**, **lexers**, **tokenizers**, and other components that advance in
124//!   discrete steps while potentially failing mid-stream.
125//! - For richer iterator-like abstractions, consider crates like
126//!   [`fallible-iterator`](https://crates.io/crates/fallible-iterator) or
127//!   [`fallible-streaming-iterator`](https://crates.io/crates/fallible-streaming-iterator).
128//!
129//! ## See also
130//!
131//! - [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) —
132//!   The standard *synchronous, fallible, pull-based* trait for reading **bytes**.
133//!   These traits generalize that idea to arbitrary item types.
134//! - [`Iterator<Item = Result<T, E>>`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) —
135//!   The idiomatic pattern for representing fallible iteration in the standard library.
136//! - [`futures::TryStream`](https://docs.rs/futures/latest/futures/stream/trait.TryStream.html) —
137//!   The *asynchronous* equivalent of this pattern.
138
139#![cfg_attr(not(feature = "std"), no_std)]
140
141#[cfg(feature = "alloc")]
142extern crate alloc;
143
144#[cfg(feature = "alloc")]
145use alloc::vec::{self, Vec};
146
147/// Context-aware, fallible producer.
148///
149/// A trait for types that can produce items one at a time with the help of
150/// an external context, where fetching the next item may fail.
151///
152/// This trait is **synchronous** — each call to [`try_next`](Self::try_next)
153/// blocks until an item is produced or an error occurs.
154///
155/// The [`Context`](Self::Context) type allows the caller to provide
156/// additional state or resources used during iteration. It can hold shared
157/// mutable state, configuration data, or external resources such as file
158/// handles, buffers, or network clients. Each call to [`try_next`](Self::try_next)
159/// receives a mutable reference to this context.
160///
161/// See the [module-level documentation](self) for details and examples.
162pub trait TryNextWithContext {
163    /// The type of items yielded by this source.
164    type Item;
165
166    /// The error type that may be returned when producing the next item fails.
167    type Error;
168
169    /// The type of context passed to each call to [`try_next`](Self::try_next).
170    type Context;
171
172    /// Attempts to produce the next item, using the provided mutable context.
173    ///
174    /// Returns:
175    /// - `Ok(Some(item))` — a new item was produced,
176    /// - `Ok(None)` — the source is exhausted,
177    /// - `Err(e)` — iteration failed with an error.
178    fn try_next_with_context(
179        &mut self,
180        context: &mut Self::Context,
181    ) -> Result<Option<Self::Item>, Self::Error>;
182
183
184    /// Collects all remaining items into a [`Vec`], using the given context.
185    ///
186    /// The method repeatedly calls [`try_next_with_context`](Self::try_next_with_context)
187    /// until `None` or an error is returned, collecting all successful items into a vector.
188    /// If any call returns `Err(e)`, iteration stops immediately and that error is returned.
189    ///
190    /// # Feature
191    /// This method is only available when the `alloc` feature is enabled.
192    #[cfg(feature = "alloc")]
193    #[inline]
194    fn try_collect_with_context(
195        &mut self,
196        context: &mut Self::Context,
197    ) -> Result<Vec<Self::Item>, Self::Error> {
198        let mut vs = Vec::new();
199        while let Some(v) = self.try_next_with_context(context)? {
200            vs.push(v);
201        }
202        Ok(vs)
203    }
204}
205
206/// Context-free, fallible producer.
207///
208/// A trait for types that can produce items one at a time, where fetching
209/// the next item may fail.
210///
211/// This trait is **synchronous** — each call to [`try_next`](Self::try_next)
212/// blocks until an item is produced or an error occurs. See the
213/// [module-level documentation](self) for details and examples.
214pub trait TryNext {
215    /// The type of items yielded by this source.
216    type Item;
217
218    /// The error type that may be returned when producing the next item fails.
219    type Error;
220
221    /// Attempts to produce the next item from the source.
222    ///
223    /// Returns:
224    /// - `Ok(Some(item))` — a new item was produced,
225    /// - `Ok(None)` — the source is exhausted,
226    /// - `Err(e)` — iteration failed with an error.
227    fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error>;
228
229    /// Collects all remaining items into a [`Vec`].
230    ///
231    /// The method repeatedly calls [`try_next`](Self::try_next) until `None` or an error
232    /// is returned, collecting all successful items into a vector.  
233    /// If any call returns `Err(e)`, iteration stops immediately and that error is returned.
234    ///
235    /// # Feature
236    /// This method is only available when the `alloc` feature is enabled.
237    #[cfg(feature = "alloc")]
238    #[inline]
239    fn try_collect(
240        &mut self,
241    ) -> Result<Vec<Self::Item>, Self::Error> {
242        let mut vs = Vec::new();
243        while let Some(v) = self.try_next()? {
244            vs.push(v);
245        }
246        Ok(vs)
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::{TryNext, TryNextWithContext};
253    use core::convert::Infallible;
254
255    /// A simple source that yields 0..limit, then `Ok(None)`.
256    struct Counter {
257        current: usize,
258        limit: usize,
259    }
260
261    impl TryNext for Counter {
262        type Item = usize;
263        type Error = Infallible;
264
265        fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
266            if self.current < self.limit {
267                let v = self.current;
268                self.current += 1;
269                Ok(Some(v))
270            } else {
271                Ok(None)
272            }
273        }
274    }
275
276    /// A source that yields 0..fail_at, then returns `Err(())`.
277    struct FailableCounter {
278        current: usize,
279        fail_at: usize,
280        failed: bool,
281    }
282
283    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
284    struct UnitErr;
285
286    impl TryNext for FailableCounter {
287        type Item = usize;
288        type Error = UnitErr;
289
290        fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
291            if self.failed {
292                // Once failed, keep failing to make behavior explicit for tests
293                return Err(UnitErr);
294            }
295            if self.current == self.fail_at {
296                self.failed = true;
297                return Err(UnitErr);
298            }
299            let v = self.current;
300            self.current += 1;
301            Ok(Some(v))
302        }
303    }
304
305    #[cfg(feature = "alloc")]
306    fn drain<S: TryNext>(mut src: S) -> Result<Vec<S::Item>, S::Error> {
307        src.try_collect()
308    }
309
310    #[test]
311    fn counter_yields_then_none() {
312        let mut c = Counter {
313            current: 0,
314            limit: 3,
315        };
316
317        assert_eq!(c.try_next().unwrap(), Some(0));
318        assert_eq!(c.try_next().unwrap(), Some(1));
319        assert_eq!(c.try_next().unwrap(), Some(2));
320        assert_eq!(c.try_next().unwrap(), None);
321
322        // Stay exhausted
323        assert_eq!(c.try_next().unwrap(), None);
324    }
325
326    #[test]
327    #[cfg(feature = "alloc")]
328    fn drain_collects_all_items() {
329        let c = Counter {
330            current: 0,
331            limit: 5,
332        };
333        let items = drain(c).unwrap();
334        assert_eq!(items, vec![0, 1, 2, 3, 4]);
335    }
336
337    #[test]
338    fn error_propagates() {
339        let mut s = FailableCounter {
340            current: 0,
341            fail_at: 2,
342            failed: false,
343        };
344
345        // First two items OK
346        assert_eq!(s.try_next(), Ok(Some(0)));
347        assert_eq!(s.try_next(), Ok(Some(1)));
348
349        // Then an error
350        assert_eq!(s.try_next(), Err(UnitErr));
351
352        // Subsequent calls keep erroring in this test source
353        assert_eq!(s.try_next(), Err(UnitErr));
354    }
355
356    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
357    struct Ctx {
358        calls: usize,
359    }
360
361    impl TryNextWithContext for Counter {
362        type Item = usize;
363        type Error = Infallible;
364        type Context = Ctx;
365
366        fn try_next_with_context(
367            &mut self,
368            ctx: &mut Self::Context,
369        ) -> Result<Option<Self::Item>, Self::Error> {
370            ctx.calls += 1;
371            if self.current < self.limit {
372                let v = self.current;
373                self.current += 1;
374                Ok(Some(v))
375            } else {
376                Ok(None)
377            }
378        }
379    }
380
381    /// Drain helper for context-aware sources; returns both the items and the
382    /// final context so the caller can assert on context changes.
383    #[cfg(feature = "alloc")]
384    fn drain_with_ctx<S: TryNextWithContext>(
385        mut src: S,
386        mut ctx: S::Context,
387    ) -> Result<(Vec<S::Item>, S::Context), S::Error> {
388        let mut out = Vec::new();
389        while let Some(item) = src.try_next_with_context(&mut ctx)? {
390            out.push(item);
391        }
392        Ok((out, ctx))
393    }
394
395    #[test]
396    #[cfg(feature = "alloc")]
397    fn context_counter_yields_and_updates_context() {
398        let src = Counter {
399            current: 0,
400            limit: 3,
401        };
402        let (items, ctx) = drain_with_ctx(src, Ctx::default()).unwrap();
403
404        // Produced the expected sequence 0, 1, 2.
405        assert_eq!(items, vec![0, 1, 2]);
406
407        // Called once per yielded item plus one final call returning None.
408        assert_eq!(ctx.calls, 4);
409    }
410}