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