Crate try_next

Crate try_next 

Source
Expand description

Minimal traits for synchronous, fallible, pull-based item sources.

This module defines three related traits:

  • TryNext<S = ()> — a context-free, fallible producer of items,
  • TryNextWithContext<C, S = ()> — a context-aware variant that allows the caller to supply mutable external state on each iteration step.
  • IterInput<I> — an input adapter that wraps any iterator and provides TryNext and TryNextWithContext<C> interface, automatically fusing the iterator

Traits TryNext<S = ()> and TryNextWithContext<C, S = ()> follow the same basic pattern: they represent a source that can attempt to produce the next item, which may succeed, fail, or signal the end of the sequence.

§Core idea

Each try_next* method call returns a Result with three possible outcomes:

  • Ok(Some(item)) — a successfully produced item,
  • Ok(None) — no more items are available (the source is exhausted),
  • Err(error) — an error occurred while trying to produce the next item.

These traits are synchronous — each call blocks until a result is ready. They are suitable for ordinary blocking or CPU-bound producers such as parsers, generators, or readers. For asynchronous, non-blocking sources, use futures::TryStream.

§Optional stats type S

Both TryNext and TryNextWithContext<C> accept an optional generic S that represents a lightweight stats snapshot for an implementation. By default, S = () and stats/stats simply return (). Implementors may choose a custom S to expose metrics (counters, flags, etc.) and override stats() to return them. The only requirement is that S: Default + Copy.

use try_next::TryNext;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
struct MyStats { calls: u32 }

struct Demo { calls: u32, left: u32 }

impl TryNext<MyStats> for Demo {
    type Item = u32;
    type Error = core::convert::Infallible;

    fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
        self.calls += 1;
        if self.left == 0 { return Ok(None); }
        let out = self.left - 1;
        self.left -= 1;
        Ok(Some(out))
    }

    fn stats(&self) -> MyStats { MyStats { calls: self.calls } }
}

§TryNext<S = ()>

The simplest case: a self-contained, fallible producer that doesn’t depend on any external context.

use try_next::TryNext;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MyError { Broken }

struct Demo { state: u8 }

impl TryNext for Demo {
    type Item = u8;
    type Error = MyError;

    fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
        match self.state {
            0..=2 => {
                let v = self.state;
                self.state += 1;
                Ok(Some(v))
            }
            3 => {
                self.state += 1;
                Ok(None)
            }
            _ => Err(MyError::Broken),
        }
    }
}

let mut src = Demo { state: 0 };
assert_eq!(src.try_next(), Ok(Some(0)));
assert_eq!(src.try_next(), Ok(Some(1)));
assert_eq!(src.try_next(), Ok(Some(2)));
assert_eq!(src.try_next(), Ok(None));
assert_eq!(src.try_next(), Err(MyError::Broken));

§TryNextWithContext<C, S = ()>

A generalization of TryNext<S = ()> that allows each call to [try_next_with_context] to receive a mutable reference to a caller-supplied context.

The context can hold shared mutable state, configuration data, or external resources such as file handles, buffers, or clients. This pattern is useful when the producer needs external help or coordination to produce the next item, while keeping the trait itself simple and generic.

use try_next::TryNextWithContext;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MyError { Fail }

struct Producer;

struct Ctx { next_val: u8 }

impl TryNextWithContext<Ctx> for Producer {
    type Item = u8;
    type Error = MyError;

    fn try_next_with_context(
        &mut self,
        ctx: &mut Ctx,
    ) -> Result<Option<Self::Item>, Self::Error> {
        if ctx.next_val < 3 {
            let v = ctx.next_val;
            ctx.next_val += 1;
            Ok(Some(v))
        } else {
            Ok(None)
        }
    }
}

let mut producer = Producer;
let mut ctx = Ctx { next_val: 0 };

assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(0)));
assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(1)));
assert_eq!(producer.try_next_with_context(&mut ctx), Ok(Some(2)));
assert_eq!(producer.try_next_with_context(&mut ctx), Ok(None));

§IterInput<I>

A simple TryNextWithContext<C, S = ()> adapter for ordinary Rust iterators.

IterInput wraps any Iterator and exposes it as a TryNextWithContext<C, S = ()> producer that never fails and ignores the provided context. Internally, the iterator is automatically fused — once it yields None, all subsequent calls also return None.

This is useful for integrating plain iterators into APIs or components that expect a context-aware, fallible producer, without changing their semantics.

§Example

use try_next::TryNextWithContext;
use try_next::IterInput;

struct DummyCtx;

let data = [10, 20, 30];
let mut input = IterInput::from(data.into_iter());
let mut ctx = DummyCtx;

assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(10));
assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(20));
assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), Some(30));
assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None);
assert_eq!(input.try_next_with_context(&mut ctx).unwrap(), None); // fused

§Notes

  • The C type parameter exists for trait compatibility but is not used.
  • The error type is always [Infallible], as the wrapped iterator cannot fail.
  • Ideal for testing or bridging APIs that use TryNextWithContext<C> but only need to pull from a fixed iterator.
  • The optional stats type parameter S on the traits is independent of the iterator and commonly left as the default ().

§Design notes

  • All traits are deliberately minimal: they define no combinators or adapters. Their purpose is to provide a simple, low-level interface for fallible, stepwise data production.
  • TryNextWithContext<C, S = ()> can often serve as a building block for adapters that integrate external state or resources.
  • These traits are a good fit for incremental or stateful producers such as parsers, lexers, tokenizers, and other components that advance in discrete steps while potentially failing mid-stream.
  • For richer iterator-like abstractions, consider crates like fallible-iterator or fallible-streaming-iterator.

§See also

  • std::io::Read — The standard synchronous, fallible, pull-based trait for reading bytes. These traits generalize that idea to arbitrary item types.
  • Iterator<Item = Result<T, E>> — The idiomatic pattern for representing fallible iteration in the standard library.
  • futures::TryStream — The asynchronous equivalent of this pattern.

Modules§

io
I/O utility implementations for TryNext and TryNextWithContext.

Structs§

IterInput
An input adapter that wraps any iterator and provides TryNext and TryNextWithContext<C> interface, automatically fusing the iterator so it never yields items after returning None once.

Traits§

TryNext
Context-free, fallible producer.
TryNextWithContext
Context-aware, fallible producer.