solar_data_structures/collect.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
//! Modified from [`rustc_type_id`](https://github.com/rust-lang/rust/blob/b389b0ab72cb0aa9acf4df0ae0c0e12090782da9/compiler/rustc_type_ir/src/interner.rs#L305).
use smallvec::SmallVec;
/// Implements `f(&iter.collect::<Vec<_>>())` more efficiently.
///
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
/// that produces `T` items. You could combine them with
/// `f(&iter.collect::<Vec<_>>())`, but this requires allocating memory for the
/// `Vec`.
///
/// This trait allows for faster implementations, intended for cases where the
/// number of items produced by the iterator is small. There is a blanket impl
/// for `T` items, but there is also a fallible impl for `Result<T, E>` items.
pub trait CollectAndApply<T, R>: Sized {
type Output;
/// Produce a result of type `Self::Output` from `iter`. The result will
/// typically be produced by applying `f` on the elements produced by
/// `iter`, though this may not happen in some impls, e.g. if an error
/// occurred during iteration.
fn collect_and_apply<I, F>(iter: I, f: F) -> Self::Output
where
I: Iterator<Item = Self>,
F: FnOnce(&[T]) -> R;
}
/// The blanket impl that always collects all elements and applies `f`.
impl<T, R> CollectAndApply<T, R> for T {
type Output = R;
/// Equivalent to `f(&iter.collect::<Vec<_>>())`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> R
where
I: Iterator<Item = T>,
F: FnOnce(&[T]) -> R,
{
// This code is hot enough that it's worth specializing for the most
// common length lists, to avoid the overhead of `SmallVec` creation.
// Lengths 0, 1, and 2 typically account for ~95% of cases. If
// `size_hint` is incorrect a panic will occur via an `unwrap` or an
// `assert`.
match iter.size_hint() {
(0, Some(0)) => {
assert!(iter.next().is_none());
f(&[])
}
(1, Some(1)) => {
let t0 = iter.next().unwrap();
assert!(iter.next().is_none());
f(&[t0])
}
(2, Some(2)) => {
let t0 = iter.next().unwrap();
let t1 = iter.next().unwrap();
assert!(iter.next().is_none());
f(&[t0, t1])
}
_ => f(&iter.collect::<SmallVec<[_; 8]>>()),
}
}
}
/// A fallible impl that will fail, without calling `f`, if there are any
/// errors during collection.
impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
type Output = Result<R, E>;
/// Equivalent to `Ok(f(&iter.collect::<Result<Vec<_>>>()?))`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E>
where
I: Iterator<Item = Self>,
F: FnOnce(&[T]) -> R,
{
// This code is hot enough that it's worth specializing for the most
// common length lists, to avoid the overhead of `SmallVec` creation.
// Lengths 0, 1, and 2 typically account for ~95% of cases. If
// `size_hint` is incorrect a panic will occur via an `unwrap` or an
// `assert`, unless a failure happens first, in which case the result
// will be an error anyway.
Ok(match iter.size_hint() {
(0, Some(0)) => {
assert!(iter.next().is_none());
f(&[])
}
(1, Some(1)) => {
let t0 = iter.next().unwrap()?;
assert!(iter.next().is_none());
f(&[t0])
}
(2, Some(2)) => {
let t0 = iter.next().unwrap()?;
let t1 = iter.next().unwrap()?;
assert!(iter.next().is_none());
f(&[t0, t1])
}
_ => f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?),
})
}
}