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}