Expand description
§empty-fallback-chain
Simple rust library that provides an iterator-combinator - similar to Chain
- that takes two iterators and glues them together, one after the other. However, unlike that combinator, this one will only run through the second iterator if the first iterator never produces any value. To get started, see IteratorExt::empty_fallback_chain
.
This is highly useful when you have a collection of iterators and want to take values from the first one that produces something. The combinator in this library is guaranteed not to attempt to read any values from the second iterator unless the first iterator produces nothing, so it is very good at being lazy.
§Basic Example
use empty_fallback_chain::IteratorExt as _;
fn empty_fallback(first: impl IntoIterator<Item = usize>, second: impl IntoIterator<Item = usize>) -> Vec<usize> {
first.into_iter().empty_fallback_chain(second).collect::<Vec<_>>()
}
assert_eq!(empty_fallback([1, 2, 3], [4, 5, 6]), vec![1, 2, 3]);
assert_eq!(empty_fallback([], [4, 5, 6]), vec![4, 5, 6]);
§Basic Conditional Fallback Example
This illustrates that it’s relatively easy to also conditionally apply fallback, with a unified iterator type lacking unnecessary indirection.
use empty_fallback_chain::IteratorExt as _;
use core::iter;
fn apply_fallback_conditionally(
a: impl IntoIterator<Item = usize>,
do_fallback: bool
) -> impl Iterator<Item = usize> {
if do_fallback {
a.into_iter().empty_fallback_chain([3, 4, 5].iter().copied())
} else {
// Note here that we need to provide some help with deduction by providing the
// outermost iterator of the chain as a hint, because `maybe_empty_fallback_chain`
// takes an `IntoIterator` as it's parameter and then uses the associated
// `IntoIter` type, rather than only taking `Iterator`s
//
// Another way to solve this problem is to only use one call to
// `maybe_empty_fallback_chain`, and unify the `Some` and `None` cases into a
// single `Option` beforehand.
a.into_iter().maybe_empty_fallback_chain::<iter::Copied<_>>(None)
}
}
assert_eq!(apply_fallback_conditionally([1, 2], true).collect::<Vec<_>>(), vec![1, 2]);
assert_eq!(apply_fallback_conditionally([], true).collect::<Vec<_>>(), vec![3, 4, 5]);
assert_eq!(apply_fallback_conditionally([1, 2], false).collect::<Vec<_>>(), vec![1, 2]);
assert_eq!(apply_fallback_conditionally([], false).collect::<Vec<_>>(), vec![]);
§Performance & Optimisation Notes
The code in this library also takes a lot of implementation infrastructure from inside the Standard Library’s implementation of Chain
. This is important, because - especially with nested chaining - it would be very easy to accidentally create very challenging-to-optimise call chains.
By using the work the Rust Standard Library Maintainers have already done to make their own implementation “optimiser friendly”, we can hopefully avoid many performance pitfalls.
§Future Possibilities
In future, it might be possible to implement more powerful features, like the ability to do some kind of interesting threshold chaining where you can pick the first “k” of “n” iterators.
The main way I can see to do this is to make some sort of macro that creates a structure with a threshold counter. But that’s not the focus right now, and this library will likely reach some sort of “complete” status in very near future unless there is a pressing need for this functionality.
Modules§
Structs§
- Empty
Fallback Chain - An iterator that links two iterators together, in a chain, if the first produces nothing.
Traits§
- Iterator
Ext - Trait for extending
Iterator
s with methods to createEmptyFallbackChain
iterators.
Functions§
- empty_
fallback_ chain - Construct an
EmptyFallbackChain
iterator. SeeIteratorExt::empty_fallback_chain
for more information. - maybe_
empty_ fallback_ chain - Construct an
EmptyFallbackChain
iterator with the second one optionally “pre-emptively made empty” - pre_
empty_ fallback_ chain - Create an
EmptyFallbackChain
with (optionally) one or more of the iterators set to “empty” pre-emptively, by providing it asNone
.