Crate empty_fallback_chain

Source
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§

prelude

Structs§

EmptyFallbackChain
An iterator that links two iterators together, in a chain, if the first produces nothing.

Traits§

IteratorExt
Trait for extending Iterators with methods to create EmptyFallbackChain iterators.

Functions§

empty_fallback_chain
Construct an EmptyFallbackChain iterator. See IteratorExt::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 as None.