try_next/lib.rs
1//! Minimal traits for synchronous, fallible, pull-based item sources.
2//!
3//! This module defines three related traits:
4//!
5//! - [`TryNext<S = ()>`] — a context-free, fallible producer of items,
6//! - [`TryNextWithContext<C, S = ()>`] — a context-aware variant that allows the caller
7//! to supply mutable external state on each iteration step.
8//! - [`IterInput<I>`] — an input adapter that wraps any iterator
9//! and provides `TryNext` and `TryNextWithContext<C>` interface, automatically
10//! fusing the iterator
11//!
12//! Traits [`TryNext<S = ()>`] and [`TryNextWithContext<C, S = ()>`] follow the same basic pattern:
13//! they represent a source that can **attempt to produce the next item**, which may
14//! succeed, fail, or signal the end of the sequence.
15//!
16//! ## Core idea
17//!
18//! Each `try_next*` method call returns a [`Result`] with three possible outcomes:
19//!
20//! * `Ok(Some(item))` — a successfully produced item,
21//! * `Ok(None)` — no more items are available (the source is exhausted),
22//! * `Err(error)` — an error occurred while trying to produce the next item.
23//!
24//! These traits are **synchronous** — each call blocks until a result is ready.
25//! They are suitable for ordinary blocking or CPU-bound producers such as parsers,
26//! generators, or readers. For asynchronous, non-blocking sources, use
27//! [`futures::TryStream`](https://docs.rs/futures/latest/futures/stream/trait.TryStream.html).
28//!
29//! ### Optional stats type `S`
30//!
31//! Both [`TryNext`] and [`TryNextWithContext<C>`] accept an **optional generic**
32//! `S` that represents a *lightweight stats snapshot* for an implementation.
33//! By default, `S = ()` and [`stats`](TryNext::stats)/[`stats`](TryNextWithContext::stats)
34//! simply return `()`. Implementors may choose a custom `S` to expose metrics
35//! (counters, flags, etc.) and override `stats()` to return them. The only
36//! requirement is that `S: Default + Copy`.
37//!
38//! ```rust
39//! use try_next::TryNext;
40//!
41//! #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
42//! struct MyStats { calls: u32 }
43//!
44//! struct Demo { calls: u32, left: u32 }
45//!
46//! impl TryNext<MyStats> for Demo {
47//! type Item = u32;
48//! type Error = core::convert::Infallible;
49//!
50//! fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
51//! self.calls += 1;
52//! if self.left == 0 { return Ok(None); }
53//! let out = self.left - 1;
54//! self.left -= 1;
55//! Ok(Some(out))
56//! }
57//!
58//! fn stats(&self) -> MyStats { MyStats { calls: self.calls } }
59//! }
60//! ```
61//!
62//! ## [`TryNext<S = ()>`]
63//!
64//! The simplest case: a self-contained, fallible producer that doesn’t depend on
65//! any external context.
66//!
67//! ```rust
68//! use try_next::TryNext;
69//!
70//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
71//! enum MyError { Broken }
72//!
73//! struct Demo { state: u8 }
74//!
75//! impl TryNext for Demo {
76//! type Item = u8;
77//! type Error = MyError;
78//!
79//! fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
80//! match self.state {
81//! 0..=2 => {
82//! let v = self.state;
83//! self.state += 1;
84//! Ok(Some(v))
85//! }
86//! 3 => {
87//! self.state += 1;
88//! Ok(None)
89//! }
90//! _ => Err(MyError::Broken),
91//! }
92//! }
93//! }
94//!
95//! let mut src = Demo { state: 0 };
96//! assert_eq!(src.try_next(), Ok(Some(0)));
97//! assert_eq!(src.try_next(), Ok(Some(1)));
98//! assert_eq!(src.try_next(), Ok(Some(2)));
99//! assert_eq!(src.try_next(), Ok(None));
100//! assert_eq!(src.try_next(), Err(MyError::Broken));
101//! ```
102//!
103//! ## [`TryNextWithContext<C, S = ()>`]
104//!
105//! A generalization of [`TryNext<S = ()>`] that allows each call to [`try_next_with_context`]
106//! to receive a mutable reference to a caller-supplied **context**.
107//!
108//! The context can hold shared mutable state, configuration data, or external
109//! resources such as file handles, buffers, or clients. This pattern is useful when
110//! the producer needs external help or coordination to produce the next item, while
111//! keeping the trait itself simple and generic.
112//!
113//! ```rust
114//! use try_next::TryNextWithContext;
115//!
116//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
117//! enum MyError { Fail }
118//!
119//! struct Producer;
120//!
121//! struct Ctx { next_val: u8 }
122//!
123//! impl TryNextWithContext<Ctx> for Producer {
124//! type Item = u8;
125//! type Error = MyError;
126//!
127//! fn try_next_with_context(
128//! &mut self,
129//! ctx: &mut Ctx,
130//! ) -> Result<Option<Self::Item>, Self::Error> {
131//! if ctx.next_val < 3 {
132//! let v = ctx.next_val;
133//! ctx.next_val += 1;
134//! Ok(Some(v))
135//! } else {
136//! Ok(None)
137//! }
138//! }
139//! }
140//!
141//! let mut producer = Producer;
142//! let mut ctx = Ctx { next_val: 0 };
143//!
144//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(0)));
145//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(1)));
146//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(2)));
147//! assert_eq!(producer.try_next_with_context(&mut ctx), Ok(None));
148//! ```
149//!
150//! ## [`IterInput<I>`]
151//!
152//! A simple [`TryNextWithContext<C, S = ()>`] adapter for ordinary Rust iterators.
153//!
154//! `IterInput` wraps any [`Iterator`] and exposes it as a
155//! [`TryNextWithContext<C, S = ()>`] producer that never fails and ignores the provided context.
156//! Internally, the iterator is automatically *fused* — once it yields `None`,
157//! all subsequent calls also return `None`.
158//!
159//! This is useful for integrating plain iterators into APIs or components that
160//! expect a context-aware, fallible producer, without changing their semantics.
161//!
162//! ### Example
163//!
164//! ```rust
165//! use try_next::TryNextWithContext;
166//! use try_next::IterInput;
167//!
168//! struct DummyCtx;
169//!
170//! let data = [10, 20, 30];
171//! let mut input = IterInput::from(data.into_iter());
172//! let mut ctx = DummyCtx;
173//!
174//! assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(10));
175//! assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(20));
176//! assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(30));
177//! assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
178//! assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None); // fused
179//! ```
180//!
181//! ### Notes
182//!
183//! - The `C` type parameter exists for trait compatibility but is not used.
184//! - The error type is always [`Infallible`], as the wrapped iterator cannot fail.
185//! - Ideal for testing or bridging APIs that use [`TryNextWithContext<C>`] but only
186//! need to pull from a fixed iterator.
187//! - The optional stats type parameter `S` on the traits is independent of the
188//! iterator and commonly left as the default `()`.
189//!
190//! ## Design notes
191//!
192//! - All traits are deliberately **minimal**: they define no combinators or adapters.
193//! Their purpose is to provide a simple, low-level interface for fallible, stepwise
194//! data production.
195//! - `TryNextWithContext<C, S = ()>` can often serve as a building block for adapters that
196//! integrate external state or resources.
197//! - These traits are a good fit for *incremental* or *stateful* producers such as
198//! **parsers**, **lexers**, **tokenizers**, and other components that advance in
199//! discrete steps while potentially failing mid-stream.
200//! - For richer iterator-like abstractions, consider crates like
201//! [`fallible-iterator`](https://crates.io/crates/fallible-iterator) or
202//! [`fallible-streaming-iterator`](https://crates.io/crates/fallible-streaming-iterator).
203//!
204//! ## See also
205//!
206//! - [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) —
207//! The standard *synchronous, fallible, pull-based* trait for reading **bytes**.
208//! These traits generalize that idea to arbitrary item types.
209//! - [`Iterator<Item = Result<T, E>>`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) —
210//! The idiomatic pattern for representing fallible iteration in the standard library.
211//! - [`futures::TryStream`](https://docs.rs/futures/latest/futures/stream/trait.TryStream.html) —
212//! The *asynchronous* equivalent of this pattern.
213
214#![cfg_attr(not(feature = "std"), no_std)]
215
216#[cfg(feature = "alloc")]
217extern crate alloc;
218
219#[cfg(feature = "alloc")]
220use alloc::vec::{self, Vec};
221
222/// Context-aware, fallible producer.
223///
224/// A trait for types that can produce items one at a time with the help of
225/// an external context, where fetching the next item may fail.
226///
227/// This trait is **synchronous** — each call to [`try_next`](Self::try_next)
228/// blocks until an item is produced or an error occurs.
229///
230/// The context type `C` allows the caller to provide additional
231/// state or resources used during iteration. It can hold shared
232/// mutable state, configuration data, or external resources such as file
233/// handles, buffers, or network clients. Each call to [`try_next`](Self::try_next)
234/// receives a mutable reference to this context.
235///
236/// # Type Parameters
237///
238/// - `C` - The type of the external context passed to each call of
239/// [`try_next_with_context`](Self::try_next_with_context). It represents the
240/// environment or state that the producer can use or mutate while producing
241/// items. For example, this might be a reader, a buffer pool, or a user-defined
242/// structure containing shared resources.
243/// - `S` - Optional stats type.
244pub trait TryNextWithContext<C, S = ()>
245where
246 S: Default + Copy,
247{
248 /// The type of items yielded by this source.
249 type Item;
250
251 /// The error type that may be returned when producing the next item fails.
252 type Error;
253
254 /// Attempts to produce the next item, using the provided mutable context.
255 ///
256 /// Returns:
257 /// - `Ok(Some(item))` — a new item was produced,
258 /// - `Ok(None)` — the source is exhausted,
259 /// - `Err(e)` — iteration failed with an error.
260 fn try_next_with_context(&mut self, context: &mut C)
261 -> Result<Option<Self::Item>, Self::Error>;
262
263 /// Collects all remaining items into a [`Vec`], using the given context.
264 ///
265 /// The method repeatedly calls [`try_next_with_context`](Self::try_next_with_context)
266 /// until `None` or an error is returned, collecting all successful items into a vector.
267 /// If any call returns `Err(e)`, iteration stops immediately and that error is returned.
268 ///
269 /// # Feature
270 /// This method is only available when the `alloc` feature is enabled.
271 #[cfg(feature = "alloc")]
272 #[inline]
273 fn try_collect_with_context(
274 &mut self,
275 context: &mut C,
276 ) -> Result<Vec<Self::Item>, Self::Error> {
277 let mut vs = Vec::new();
278 while let Some(v) = self.try_next_with_context(context)? {
279 vs.push(v);
280 }
281 Ok(vs)
282 }
283
284 fn stats(&self) -> S {
285 S::default()
286 }
287}
288
289/// Context-free, fallible producer.
290///
291/// A trait for types that can produce items one at a time, where fetching
292/// the next item may fail.
293///
294/// This trait is **synchronous** — each call to [`try_next`](Self::try_next)
295/// blocks until an item is produced or an error occurs. See the
296/// [module-level documentation](self) for details and examples.
297///
298/// - `S` - Optional stats type.
299pub trait TryNext<S = ()>
300where
301 S: Default + Copy,
302{
303 /// The type of items yielded by this source.
304 type Item;
305
306 /// The error type that may be returned when producing the next item fails.
307 type Error;
308
309 /// Attempts to produce the next item from the source.
310 ///
311 /// Returns:
312 /// - `Ok(Some(item))` — a new item was produced,
313 /// - `Ok(None)` — the source is exhausted,
314 /// - `Err(e)` — iteration failed with an error.
315 fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error>;
316
317 /// Collects all remaining items into a [`Vec`].
318 ///
319 /// The method repeatedly calls [`try_next`](Self::try_next) until `None` or an error
320 /// is returned, collecting all successful items into a vector.
321 /// If any call returns `Err(e)`, iteration stops immediately and that error is returned.
322 ///
323 /// # Feature
324 /// This method is only available when the `alloc` feature is enabled.
325 #[cfg(feature = "alloc")]
326 #[inline]
327 fn try_collect(&mut self) -> Result<Vec<Self::Item>, Self::Error> {
328 let mut vs = Vec::new();
329 while let Some(v) = self.try_next()? {
330 vs.push(v);
331 }
332 Ok(vs)
333 }
334
335 fn stats(&self) -> S {
336 S::default()
337 }
338}
339
340/// An input adapter that wraps any iterator and provides `TryNext` and
341/// `TryNextWithContext<C>` interface, automatically fusing the iterator
342/// so it never yields items after returning `None` once.
343///
344/// # Type Parameters
345///
346/// - `I`: The underlying iterator type. It can be any `Iterator`.
347/// - `C`: The *context* type, which is passed by mutable reference to each
348/// `try_next_with_context` call.
349pub struct IterInput<I>
350where
351 I: Iterator,
352{
353 /// The underlying fused iterator.
354 iter: core::iter::Fuse<I>,
355}
356
357impl<I> IterInput<I>
358where
359 I: Iterator,
360{
361 /// Creates a new `IterInput` from any iterator.
362 ///
363 /// The iterator is automatically fused internally, so that once it returns
364 /// `None`, all further `next()` calls will also return `None`.
365 pub fn from(iter: I) -> Self {
366 Self { iter: iter.fuse() }
367 }
368}
369
370impl<I> TryNext for IterInput<I>
371where
372 I: Iterator,
373{
374 type Item = I::Item;
375 type Error = core::convert::Infallible;
376
377 #[inline]
378 fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
379 Ok(self.iter.next())
380 }
381}
382
383impl<I, C> TryNextWithContext<C> for IterInput<I>
384where
385 I: Iterator,
386{
387 type Item = I::Item;
388 type Error = core::convert::Infallible;
389
390 #[inline]
391 fn try_next_with_context(
392 &mut self,
393 _context: &mut C,
394 ) -> Result<Option<Self::Item>, Self::Error> {
395 Ok(self.iter.next())
396 }
397}
398
399#[cfg(feature = "std")]
400pub mod io;
401
402#[cfg(test)]
403mod tests {
404 use super::{IterInput, TryNext, TryNextWithContext};
405 use core::convert::Infallible;
406
407 /// A simple source that yields 0..limit, then `Ok(None)`.
408 struct Counter {
409 current: usize,
410 limit: usize,
411 }
412
413 impl TryNext for Counter {
414 type Item = usize;
415 type Error = Infallible;
416
417 fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
418 if self.current < self.limit {
419 let v = self.current;
420 self.current += 1;
421 Ok(Some(v))
422 } else {
423 Ok(None)
424 }
425 }
426 }
427
428 /// A source that yields 0..fail_at, then returns `Err(())`.
429 struct FailableCounter {
430 current: usize,
431 fail_at: usize,
432 failed: bool,
433 }
434
435 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
436 struct UnitErr;
437
438 impl TryNext for FailableCounter {
439 type Item = usize;
440 type Error = UnitErr;
441
442 fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
443 if self.failed {
444 // Once failed, keep failing to make behavior explicit for tests
445 return Err(UnitErr);
446 }
447 if self.current == self.fail_at {
448 self.failed = true;
449 return Err(UnitErr);
450 }
451 let v = self.current;
452 self.current += 1;
453 Ok(Some(v))
454 }
455 }
456
457 #[cfg(feature = "alloc")]
458 fn drain<S: TryNext>(mut src: S) -> Result<Vec<S::Item>, S::Error> {
459 src.try_collect()
460 }
461
462 #[test]
463 fn counter_yields_then_none() {
464 let mut c = Counter {
465 current: 0,
466 limit: 3,
467 };
468
469 assert_eq!(c.try_next().unwrap(), Some(0));
470 assert_eq!(c.try_next().unwrap(), Some(1));
471 assert_eq!(c.try_next().unwrap(), Some(2));
472 assert_eq!(c.try_next().unwrap(), None);
473
474 // Stay exhausted
475 assert_eq!(c.try_next().unwrap(), None);
476 }
477
478 #[test]
479 #[cfg(feature = "alloc")]
480 fn drain_collects_all_items() {
481 let c = Counter {
482 current: 0,
483 limit: 5,
484 };
485 let items = drain(c).unwrap();
486 assert_eq!(items, vec![0, 1, 2, 3, 4]);
487 }
488
489 #[test]
490 fn error_propagates() {
491 let mut s = FailableCounter {
492 current: 0,
493 fail_at: 2,
494 failed: false,
495 };
496
497 // First two items OK
498 assert_eq!(s.try_next(), Ok(Some(0)));
499 assert_eq!(s.try_next(), Ok(Some(1)));
500
501 // Then an error
502 assert_eq!(s.try_next(), Err(UnitErr));
503
504 // Subsequent calls keep erroring in this test source
505 assert_eq!(s.try_next(), Err(UnitErr));
506 }
507
508 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
509 struct Ctx {
510 calls: usize,
511 }
512
513 impl TryNextWithContext<Ctx> for Counter {
514 type Item = usize;
515 type Error = Infallible;
516
517 fn try_next_with_context(
518 &mut self,
519 ctx: &mut Ctx,
520 ) -> Result<Option<Self::Item>, Self::Error> {
521 ctx.calls += 1;
522 if self.current < self.limit {
523 let v = self.current;
524 self.current += 1;
525 Ok(Some(v))
526 } else {
527 Ok(None)
528 }
529 }
530 }
531
532 /// Drain helper for context-aware sources; returns both the items and the
533 /// final context so the caller can assert on context changes.
534 #[cfg(feature = "alloc")]
535 fn drain_with_ctx<C, S: TryNextWithContext<C>>(
536 mut src: S,
537 mut ctx: C,
538 ) -> Result<(Vec<S::Item>, C), S::Error> {
539 let mut out = Vec::new();
540 while let Some(item) = src.try_next_with_context(&mut ctx)? {
541 out.push(item);
542 }
543 Ok((out, ctx))
544 }
545
546 #[test]
547 #[cfg(feature = "alloc")]
548 fn context_counter_yields_and_updates_context() {
549 let src = Counter {
550 current: 0,
551 limit: 3,
552 };
553 let (items, ctx) = drain_with_ctx(src, Ctx::default()).unwrap();
554
555 // Produced the expected sequence 0, 1, 2.
556 assert_eq!(items, vec![0, 1, 2]);
557
558 // Called once per yielded item plus one final call returning None.
559 assert_eq!(ctx.calls, 4);
560 }
561
562 #[derive(Default)]
563 struct DummyCtx;
564
565 #[test]
566 fn iter_input_yields_all_items_from_array() {
567 let mut input = IterInput::from([1, 2, 3].into_iter());
568 let mut ctx = DummyCtx;
569
570 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(1));
571 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(2));
572 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(3));
573 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
574 }
575
576 #[test]
577 fn iter_input_is_fused_after_exhaustion() {
578 let mut input = IterInput::from(0..1);
579 let mut ctx = DummyCtx;
580
581 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(0));
582 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
583 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
584 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
585 }
586
587 #[test]
588 fn iter_input_with_empty_range_returns_none() {
589 let mut input = IterInput::from(0..0);
590 let mut ctx = DummyCtx;
591
592 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
593 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
594 }
595
596 #[test]
597 fn iter_input_over_string_bytes() {
598 let mut input = IterInput::from("hello, world!".bytes());
599 let mut ctx = DummyCtx;
600
601 // Collect bytes manually via try_next_with_context
602 let mut collected = [0u8; 13];
603 let mut count = 0;
604
605 while let Some(byte) = input.try_next_with_context(&mut ctx).unwrap() {
606 collected[count] = byte;
607 count += 1;
608 }
609
610 assert_eq!(&collected[..count], b"hello, world!");
611 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
612 assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
613 }
614
615 #[test]
616 fn stats_default_is_unit() {
617 // With the default S=() the blanket stats() should return ().
618 let c = Counter {
619 current: 0,
620 limit: 1,
621 };
622 let unit_stats: () = TryNext::stats(&c);
623 assert_eq!(unit_stats, ());
624 }
625
626 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
627 struct MyStats {
628 calls: usize,
629 }
630
631 /// A tiny source that tracks the number of `try_next` calls in `stats()`.
632 struct StatsSource {
633 remaining: usize,
634 emitted: usize,
635 call_count: usize,
636 }
637
638 impl TryNext<MyStats> for StatsSource {
639 type Item = usize;
640 type Error = Infallible;
641
642 fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
643 self.call_count += 1;
644 if self.remaining == 0 {
645 return Ok(None);
646 }
647 let v = self.emitted;
648 self.emitted += 1;
649 self.remaining -= 1;
650 Ok(Some(v))
651 }
652
653 fn stats(&self) -> MyStats {
654 MyStats {
655 calls: self.call_count,
656 }
657 }
658 }
659
660 #[test]
661 fn custom_stats_are_reported() {
662 let mut s = StatsSource {
663 remaining: 3,
664 emitted: 0,
665 call_count: 0,
666 };
667 assert_eq!(s.stats(), MyStats { calls: 0 });
668
669 // 3 items followed by None => 4 total calls
670 assert_eq!(s.try_next().unwrap(), Some(0));
671 assert_eq!(s.try_next().unwrap(), Some(1));
672 assert_eq!(s.try_next().unwrap(), Some(2));
673 assert_eq!(s.try_next().unwrap(), None);
674
675 assert_eq!(s.stats(), MyStats { calls: 4 });
676 }
677
678 #[test]
679 fn iter_input_try_next_works_without_context() {
680 // Ensure the context-free TryNext impl behaves as expected.
681 let mut input = IterInput::from([10, 20, 30].into_iter());
682 assert_eq!(TryNext::try_next(&mut input).unwrap(), Some(10));
683 assert_eq!(TryNext::try_next(&mut input).unwrap(), Some(20));
684 assert_eq!(TryNext::try_next(&mut input).unwrap(), Some(30));
685 assert_eq!(TryNext::try_next(&mut input).unwrap(), None);
686 assert_eq!(TryNext::try_next(&mut input).unwrap(), None); // fused
687 }
688
689 #[test]
690 #[cfg(feature = "alloc")]
691 fn iter_input_try_collect_collects_all() {
692 let mut input = IterInput::from([7, 8, 9].into_iter());
693 let items = input.try_collect().unwrap();
694 assert_eq!(items, vec![7, 8, 9]);
695 // And it stays exhausted.
696 assert_eq!(TryNext::try_next(&mut input).unwrap(), None);
697 }
698}