[![CI](https://github.com/lpenz/autofolder/actions/workflows/ci.yml/badge.svg)](https://github.com/lpenz/autofolder/actions/workflows/ci.yml) [![coveralls](https://coveralls.io/repos/github/lpenz/autofolder/badge.svg?branch=main)](https://coveralls.io/github/lpenz/autofolder?branch=main) [![crates.io](https://img.shields.io/crates/v/autofolder)](https://crates.io/crates/autofolder) [![doc.rs](https://docs.rs/autofolder/badge.svg)](https://docs.rs/autofolder) # autofolder *autofolder* provides a single-element "folding" container that can be used to accumulate/select/etc. values in an ad-hoc fashion. ## TL;DR: [`DynFolder`] example ```rust use autofolder::*; // Create an autofolder that retains the max u32 value: let mut max = DynFolder::new(0_u32, std::cmp::max); // We can "fold-in" individual items: max.fold(3); // We can then peek at the running output: println!("Partial max is {}", max.as_ref()); // And still keep on folding by processing whole iterators: max.extend((1..=5)); // And finally consume the autofolder to get the final output value: println!("Max value is {}", max.into_inner()); ``` See also [`Min`], [`Max`] and [`MinMax`] for useful specific reducers. ## Rationale *Folding* in Rust is accomplished via the [`Iterator::fold`] method, like so: ```rust iterator.fold(initial, function); // (and this is all you can do) ``` That works well when all the data we need is provided by a single iterator. If we have a more complex logic, `fold` can't be used. *autofolder* flips this structure by being built with the initial value and the folding function, and accepting values from various types of different sources during its lifetime. ```rust let mut autofolder = Autofolder::new(initial, function); // Fold in a whole iterator, can be repeated: autofolder.extend(iterator); // Fold in an individual value: autofolder.fold(value); ``` ## Types of folders ### By binding strategy This crate provides two types of autofolders with different function binding strategies: - [`DynFolder`]: the folding function is provided as a closure that is kept in a struct field. Characteristics: - Folding function can use any type, builtin or otherwise. - Each instance can use a different folding function, provided as a constructor argument. On the flip side, we can't use `DynFolder` with [`.collect()`](Iterator::collect). - Slightly less efficient than `ImplFolder` due to the use of dynamic dispatch - we are effectively using a function pointer instead of a function call, after all. - [`ImplFolder`]: the folding function is implemented via a trait. - Folding function can only use types defined in the user crate, which is a limitation of using traits. - Each parameterized `ImplFolder`, defined by the pair of types, can only have one folding function. Because of that, we can use `ImplFolder` with [`.collect()`](Iterator::collect) if the `output` type implements [`Default`] - Slighly more efficient than `DynFolder` due to monomorphization, which turns `.fold` calls into direct function calls. ### By aggregation strategy *Reduce* in rust is a special kind of folding where the aggregator and the item types of the folding function are the same (`Fn(Item, Item) -> Item`). That allows us to set the internal autofolder state with the first yielded value, without calling the corresponding function. This crate provides the following "autoreducer" types: - [`DynReduce`]: the reduce function is implemented via a trait. - Similar to [`DynFolder`]. - [`.into_inner()`](DynReduce::into_inner) returns an [`Option`]. - Constructor takes the `reduce` function. - [`ImplReduce`]: the reduce function is implemented via a trait. - Similar to [`ImplFolder`]. - [`.into_inner()`](ImplReduce::into_inner) returns an [`Option`]. - Implements [`.collect()`](Iterator::collect) even when the type parameters don't implement [`Default`]. ### Specific autofolders This create also provides some built-in autofolders for specific functions: - [`Min`]: container that keeps only the minimal value iterated, as given by [`std::cmp::PartialOrd`]. - [`Max`]: analogous to `Max`, but for the max value. - [`MinMax`]: container that keeps a tuple with both the min and max values. [`Iterator::fold`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold [`DynFolder`]: https://docs.rs/autofolder/latest/autofolder/struct.DynFolder.html [`DynReduce`]: https://docs.rs/autofolder/latest/autofolder/struct.DynReduce.html [`ImplFolder`]: https://docs.rs/autofolder/latest/autofolder/struct.ImplFolder.html [`ImplReduce`]: https://docs.rs/autofolder/latest/autofolder/struct.ImplReduce.html [`Default`]: https://doc.rust-lang.org/nightly/core/default/trait.Default.html [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html [Iterator::collect]: https://doc.rust-lang.org/nightly/core/iter/traits/iterator/trait.Iterator.html#method.collect [DynReduce::into_inner]: https://docs.rs/autofolder/latest/autofolder/struct.DynReduce.html#method.into_inner [ImplReduce::into_inner]: https://docs.rs/autofolder/latest/autofolder/struct.ImplReduce.html#method.into_inner [`Min`]: https://docs.rs/autofolder/latest/autofolder/struct.Min.html [`Max`]: https://docs.rs/autofolder/latest/autofolder/struct.Max.html [`MinMax`]: https://docs.rs/autofolder/latest/autofolder/struct.MinMax.html [`std::cmp::PartialOrd`]: https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html