better_collect/traits/ref_collector.rs
1use std::ops::ControlFlow;
2
3use crate::{Collector, Combine, Funnel, IntoCollector, assert_collector, assert_ref_collector};
4
5/// A [`Collector`] that can also collect items by mutable reference.
6///
7/// This trait introduces one additional method, [`collect_ref`](RefCollector::collect_ref),
8/// which takes a mutable reference to an item.
9///
10/// It exists primarily to support [`combine()`].
11/// Since [`Collector`] consumes items by ownership, each item cannot normally be passed further.
12/// A type implementing this trait essentially declares: “A view of an item is enough for me
13/// to collect it. Feel free to keep using it elsewhere.”
14/// This enables items to flow through multiple collectors while maintaining composability.
15/// See [`combine()`] for a deeper explanation.
16///
17/// # Difference from [`Collector<Item = &mut T>`]
18///
19/// Although both can collect mutable references, [`Collector<Item = &mut T>`]
20/// implies ownership of those references and their lifetimes.
21/// As such, it cannot be safely fed with references to items that will later be consumed.
22///
23/// For example, imagine a `Vec<&mut T>` collector:
24/// it would hold the references beyond a single iteration,
25/// preventing the item from being passed to another collector.
26/// [`RefCollector`], in contrast, borrows mutably just long enough to collect,
27/// then immediately releases the borrow, enabling true chaining.
28///
29/// # Dyn Compatibility
30///
31/// This trait is *dyn-compatible*, meaning it can be used as a trait object.
32/// You do not need to specify the [`Output`](crate::Collector::Output) type;
33/// providing the [`Item`] type is enough.
34///
35/// For example:
36///
37/// ```no_run
38/// # use better_collect::prelude::*;
39/// # fn foo(_:
40/// &mut dyn RefCollector<Item = i32>
41/// # ) {}
42/// ```
43///
44/// With the same [`Item`] type, a `dyn RefCollector` can be upcast to
45/// a `dyn Collector`.
46///
47/// ```no_run
48/// use better_collect::prelude::*;
49///
50/// let ref_collector: &mut dyn RefCollector<Item = i32> = &mut vec![].into_collector();
51/// let collector: &mut dyn Collector<Item = i32> = ref_collector; // upcast
52/// ```
53///
54/// [`combine()`]: RefCollector::combine
55/// [`Item`]: crate::Collector::Item
56pub trait RefCollector: Collector {
57 /// Collects an item and returns a [`ControlFlow`] indicating whether
58 /// the collector has stopped accumulating right after this operation.
59 ///
60 /// See [`Collector::collect()`] for requirements regarding the returned [`ControlFlow`].
61 ///
62 /// After implementing this method, [`Collector::collect()`] can generally be forwarded
63 /// like this:
64 ///
65 /// ```no_run
66 /// # use better_collect::prelude::*;
67 /// # use std::ops::ControlFlow;
68 /// # struct Foo;
69 /// # impl Collector for Foo {
70 /// # type Item = ();
71 /// # type Output = ();
72 /// fn collect(&mut self, mut item: Self::Item) -> ControlFlow<()> {
73 /// self.collect_ref(&mut item)
74 /// }
75 /// # fn finish(self) -> Self::Output {}
76 /// # }
77 /// # impl RefCollector for Foo {
78 /// # fn collect_ref(&mut self, item: &mut Self::Item) -> ControlFlow<()> {
79 /// # ControlFlow::Continue(())
80 /// # }
81 /// # }
82 /// ```
83 fn collect_ref(&mut self, item: &mut Self::Item) -> ControlFlow<()>;
84
85 /// Use [`combine()`](RefCollector::combine).
86 #[inline]
87 #[deprecated(since = "0.3.0", note = "Use `combine()`")]
88 fn then<C>(self, other: C) -> Combine<Self, C::IntoCollector>
89 where
90 Self: Sized,
91 C: IntoCollector<Item = Self::Item>,
92 {
93 self.combine(other)
94 }
95
96 /// The most important adaptor. The reason why this crate exists.
97 ///
98 /// Creates a [`Collector`] that lets both collectors collect the same item.
99 /// For each item collected, the first collector collects the item by mutable reference,
100 /// then the second one collects it by either mutable reference or ownership.
101 /// Together, they form a pipeline where each collector processes the item in turn,
102 /// and the final one consumes by ownership.
103 ///
104 /// If the second collector implements [`RefCollector`], this adaptor implements [`RefCollector`],
105 /// allowing the chain to be extended further with additional `combine()` calls.
106 /// Otherwise, it becomes the endpoint of the pipeline.
107 ///
108 /// # Examples
109 ///
110 /// ```
111 /// use better_collect::{prelude::*, cmp::Max};
112 ///
113 /// let mut collector = vec![].into_collector().combine(Max::new());
114 ///
115 /// assert!(collector.collect(4).is_continue());
116 /// assert!(collector.collect(2).is_continue());
117 /// assert!(collector.collect(6).is_continue());
118 /// assert!(collector.collect(3).is_continue());
119 ///
120 /// assert_eq!(collector.finish(), (vec![4, 2, 6, 3], Some(6)));
121 /// ```
122 ///
123 /// Even if one collector stops, `combine()` continues as the other does.
124 /// It only stops when both collectors stop.
125 ///
126 /// ```
127 /// use better_collect::prelude::*;
128 ///
129 /// let mut collector = vec![].into_collector().take(3).combine(()); // `()` always stops collecting.
130 ///
131 /// assert!(collector.collect(()).is_continue());
132 /// assert!(collector.collect(()).is_continue());
133 /// // Since `.take(3)` only takes 3 items,
134 /// // it hints a stop right after the 3rd item is collected.
135 /// assert!(collector.collect(()).is_break());
136 /// # // Internal assertion.
137 /// # assert!(collector.collect(()).is_break());
138 ///
139 /// assert_eq!(collector.finish(), (vec![(); 3], ()));
140 /// ```
141 ///
142 /// Collectors can be chained with `combine()` as many as you want,
143 /// as long as every of them except the last implements [`RefCollector`].
144 ///
145 /// Here’s the solution to [LeetCode #1491] to demonstrate it:
146 ///
147 /// ```
148 /// use better_collect::{
149 /// prelude::*,
150 /// cmp::{Min, Max}, num::Sum, Count,
151 /// };
152 ///
153 /// # struct Solution;
154 /// impl Solution {
155 /// pub fn average(salary: Vec<i32>) -> f64 {
156 /// let (((min, max), count), sum) = salary
157 /// .into_iter()
158 /// .better_collect(
159 /// Min::new()
160 /// .copying()
161 /// .combine(Max::new().copying())
162 /// .combine(Count::new())
163 /// .combine(Sum::<i32>::new())
164 /// );
165 ///
166 /// let (min, max) = (min.unwrap(), max.unwrap());
167 /// (sum - max - min) as f64 / (count - 2) as f64
168 /// }
169 /// }
170 ///
171 /// fn correct(actual: f64, expected: f64) -> bool {
172 /// const DELTA: f64 = 1E-5;
173 /// (actual - expected).abs() <= DELTA
174 /// }
175 ///
176 /// assert!(correct(
177 /// Solution::average(vec![5, 3, 1, 2]), 2.5
178 /// ));
179 /// assert!(correct(
180 /// Solution::average(vec![1, 2, 4]), 2.0
181 /// ));
182 /// ```
183 ///
184 /// [LeetCode #1491]: https://leetcode.com/problems/average-salary-excluding-the-minimum-and-maximum-salary
185 #[inline]
186 fn combine<C>(self, other: C) -> Combine<Self, C::IntoCollector>
187 where
188 Self: Sized,
189 C: IntoCollector<Item = Self::Item>,
190 {
191 assert_collector(Combine::new(self, other.into_collector()))
192 }
193
194 /// Creates a [`RefCollector`] that maps a mutable reference to an item
195 /// into another mutable reference.
196 ///
197 /// This is used when a [`combine`] chain expects to collect `T`,
198 /// but you have a collector that collects `U`. In that case,
199 /// you can use `funnel()` to transform `U` into `T` before passing it along.
200 ///
201 /// Unlike [`Collector::map()`] or [`Collector::map_ref()`], this adaptor works
202 /// seamlessly with [`RefCollector`]s by forwarding items directly through
203 /// the [`collect_ref`] method.
204 /// This avoids cloning because the underlying collector does not need owndership of items.
205 ///
206 /// # Examples
207 ///
208 /// ```
209 /// use better_collect::prelude::*;
210 ///
211 /// let vecs = [
212 /// vec!["a".to_owned(), "b".to_owned(), "c".to_owned()],
213 /// vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
214 /// vec!["swordswoman".to_owned(), "singer".to_owned()],
215 /// ];
216 ///
217 /// let (concat_firsts, _lens) = vecs
218 /// .into_iter()
219 /// .better_collect(
220 /// ConcatString::new()
221 /// // We only need a reference to a string to concatenate.
222 /// // `funnel` lets us avoid cloning by transforming &mut Vec<_> → &mut String.
223 /// // Otherwise, we have to clone with `map_ref`.
224 /// .funnel(|v: &mut Vec<_>| &mut v[0])
225 /// .combine(vec![].into_collector().map(|v: Vec<_>| v.len()))
226 /// );
227 ///
228 /// assert_eq!(concat_firsts, "a1swordswoman");
229 /// ```
230 ///
231 /// [`collect_ref`]: RefCollector::collect_ref
232 /// [`combine`]: RefCollector::combine
233 #[inline]
234 fn funnel<F, T>(self, func: F) -> Funnel<Self, T, F>
235 where
236 Self: Sized,
237 F: FnMut(&mut T) -> &mut Self::Item,
238 {
239 assert_ref_collector(Funnel::new(self, func))
240 }
241}
242
243/// A mutable reference to a collect produce nothing.
244///
245/// This is useful when you just want to feed items to a collector without
246/// finishing it.
247impl<C: RefCollector> RefCollector for &mut C {
248 #[inline]
249 fn collect_ref(&mut self, item: &mut Self::Item) -> ControlFlow<()> {
250 C::collect_ref(self, item)
251 }
252}
253
254macro_rules! dyn_impl {
255 ($($traits:ident)*) => {
256 impl<'a, T> Collector for &mut (dyn RefCollector<Item = T> $(+ $traits)* + 'a) {
257 type Item = T;
258
259 type Output = ();
260
261 #[inline]
262 fn collect(&mut self, item: Self::Item) -> ControlFlow<()> {
263 <dyn RefCollector<Item = T>>::collect(*self, item)
264 }
265
266 #[inline]
267 fn finish(self) -> Self::Output {}
268
269 // The default implementation are sufficient.
270 }
271
272 impl<'a, T> RefCollector for &mut (dyn RefCollector<Item = T> $(+ $traits)* + 'a) {
273 #[inline]
274 fn collect_ref(&mut self, item: &mut Self::Item) -> ControlFlow<()> {
275 <dyn RefCollector<Item = T>>::collect_ref(*self, item)
276 }
277 }
278 };
279}
280
281dyn_impl!();
282dyn_impl!(Send);
283dyn_impl!(Sync);
284dyn_impl!(Send Sync);
285
286fn _dyn_compatible<T>(_: &mut dyn RefCollector<Item = T>) {}
287
288fn _upcastable_to_collector<T>(x: &mut dyn RefCollector<Item = T>) -> &mut dyn Collector<Item = T> {
289 x
290}