maybe_parallel_iterator/
lib.rs

1use std::cmp::Ordering;
2
3/// Extends iterable collections with a function to create a [`Computation`].
4///
5/// If the `rayon` feature is enabled, this will implemented and be implemented for `IntoParallelIterator`.
6///
7/// Otherwise, this will implement and be implemented for `IntoIterator`.
8pub trait IntoMaybeParallelIterator {
9    type Item;
10    #[cfg(not(feature = "rayon"))]
11    type Iter: Iterator<Item = Self::Item>;
12    #[cfg(feature = "rayon")]
13    type Iter: rayon::iter::ParallelIterator<Item = Self::Item>;
14
15    fn into_maybe_par_iter(self) -> MaybeParallelIterator<Self::Iter>;
16}
17
18/// Like [`IntoMaybeParallelIterator`] but borrows.
19pub trait IntoMaybeParallelRefIterator<'a> {
20    #[cfg(not(feature = "rayon"))]
21    type Iter: Iterator;
22    #[cfg(feature = "rayon")]
23    type Iter: rayon::iter::ParallelIterator;
24
25    fn maybe_par_iter(&'a self) -> MaybeParallelIterator<Self::Iter>;
26}
27
28/// Like [`IntoMaybeParallelIterator`] but borrows mutably.
29pub trait IntoMaybeParallelRefMutIterator<'a> {
30    #[cfg(not(feature = "rayon"))]
31    type Iter: Iterator;
32    #[cfg(feature = "rayon")]
33    type Iter: rayon::iter::ParallelIterator;
34
35    fn maybe_par_iter_mut(&'a mut self) -> MaybeParallelIterator<Self::Iter>;
36}
37
38/// An iterator that may be sequential or parallel depending on feature flags.
39#[cfg(not(feature = "rayon"))]
40#[repr(transparent)]
41pub struct MaybeParallelIterator<IT: Iterator>(IT);
42
43#[cfg(not(feature = "rayon"))]
44impl<IIT: IntoIterator> IntoMaybeParallelIterator for IIT {
45    type Item = IIT::Item;
46    type Iter = IIT::IntoIter;
47
48    fn into_maybe_par_iter(self) -> MaybeParallelIterator<Self::Iter> {
49        MaybeParallelIterator(self.into_iter())
50    }
51}
52
53#[cfg(not(feature = "rayon"))]
54impl<'a, IIT: 'a + ?Sized> IntoMaybeParallelRefIterator<'a> for IIT
55where
56    &'a IIT: IntoIterator,
57{
58    type Iter = <&'a IIT as IntoIterator>::IntoIter;
59
60    fn maybe_par_iter(&'a self) -> MaybeParallelIterator<Self::Iter> {
61        MaybeParallelIterator(self.into_iter())
62    }
63}
64
65#[cfg(not(feature = "rayon"))]
66impl<'a, IIT: 'a + ?Sized> IntoMaybeParallelRefMutIterator<'a> for IIT
67where
68    &'a mut IIT: IntoIterator,
69{
70    type Iter = <&'a mut IIT as IntoIterator>::IntoIter;
71
72    fn maybe_par_iter_mut(&'a mut self) -> MaybeParallelIterator<Self::Iter> {
73        MaybeParallelIterator(self.into_iter())
74    }
75}
76
77#[cfg(not(feature = "rayon"))]
78impl<IT: Iterator> MaybeParallelIterator<IT> {
79    pub fn collect<B: FromIterator<IT::Item>>(self) -> B
80    where
81        Self: Sized,
82    {
83        FromIterator::from_iter(self)
84    }
85
86    /// Do a computation on all items.
87    pub fn for_each<O: Fn(IT::Item)>(self, op: O) {
88        self.0.for_each(op)
89    }
90
91    /// Process at least this many items sequentially (no-op unless `rayon` feature enabled).
92    ///
93    /// # Foot-gun
94    ///
95    /// Without `rayon` feature, this can be called in places `IndexedParallelIterator` would not
96    /// apply. These uses won't compile under `rayon`.
97    pub fn with_min_sequential(self, _: usize) -> Self {
98        self
99    }
100
101    /// Map to a tuple containing the index of each item along with that item.
102    ///
103    /// # Foot-gun
104    ///
105    /// Without `rayon` feature, this can be called in places `IndexedParallelIterator` would not
106    /// apply. These uses won't compile under `rayon`.
107    pub fn enumerate(self) -> MaybeParallelIterator<std::iter::Enumerate<IT>> {
108        MaybeParallelIterator(self.0.enumerate())
109    }
110
111    /// Like iterator mapping.
112    pub fn map<O, M: Fn(IT::Item) -> O>(
113        self,
114        map: M,
115    ) -> MaybeParallelIterator<std::iter::Map<IT, M>> {
116        MaybeParallelIterator(self.0.map(map))
117    }
118
119    /// Like iterator filter mapping.
120    pub fn filter_map<O, M: Fn(IT::Item) -> Option<O>>(
121        self,
122        map: M,
123    ) -> MaybeParallelIterator<std::iter::FilterMap<IT, M>> {
124        MaybeParallelIterator(self.0.filter_map(map))
125    }
126
127    /// Like iterator flat-mapping.
128    pub fn flat_map<O: IntoIterator, M: Fn(IT::Item) -> O>(
129        self,
130        map: M,
131    ) -> MaybeParallelIterator<std::iter::FlatMap<IT, O, M>> {
132        MaybeParallelIterator(self.0.flat_map(map))
133    }
134
135    /// Like iterator find (but won't necessarily return the first match).
136    pub fn find_any<F: Fn(&IT::Item) -> bool>(mut self, f: F) -> Option<IT::Item> {
137        self.0.find(f)
138    }
139
140    /// Get the inner iterator.
141    pub fn into_inner(self) -> IT {
142        self.0
143    }
144}
145
146#[cfg(not(feature = "rayon"))]
147impl<IT: Iterator> IntoIterator for MaybeParallelIterator<IT> {
148    type Item = IT::Item;
149    type IntoIter = IT;
150
151    fn into_iter(self) -> Self::IntoIter {
152        self.into_inner()
153    }
154}
155
156/// An iterator that may be sequential or parallel depending on feature flags.
157#[cfg(feature = "rayon")]
158#[repr(transparent)]
159pub struct MaybeParallelIterator<IT: rayon::iter::ParallelIterator>(IT);
160
161#[cfg(feature = "rayon")]
162impl<IIT> IntoMaybeParallelIterator for IIT
163where
164    IIT: rayon::iter::IntoParallelIterator,
165{
166    type Item = IIT::Item;
167    type Iter = IIT::Iter;
168
169    fn into_maybe_par_iter(self) -> MaybeParallelIterator<Self::Iter> {
170        MaybeParallelIterator(self.into_par_iter())
171    }
172}
173
174#[cfg(feature = "rayon")]
175impl<'a, IIT: 'a> IntoMaybeParallelRefIterator<'a> for IIT
176where
177    IIT: rayon::iter::IntoParallelRefIterator<'a> + ?Sized,
178{
179    type Iter = IIT::Iter;
180
181    fn maybe_par_iter(&'a self) -> MaybeParallelIterator<Self::Iter> {
182        MaybeParallelIterator(self.par_iter())
183    }
184}
185
186#[cfg(feature = "rayon")]
187impl<'a, IIT: 'a> IntoMaybeParallelRefMutIterator<'a> for IIT
188where
189    IIT: rayon::iter::IntoParallelRefMutIterator<'a> + ?Sized,
190{
191    type Iter = IIT::Iter;
192
193    fn maybe_par_iter_mut(&'a mut self) -> MaybeParallelIterator<Self::Iter> {
194        MaybeParallelIterator(self.par_iter_mut())
195    }
196}
197
198/*
199#[cfg(feature = "rayon")]
200impl<T> IntoMaybeParallelIterator for Range<T>
201    where
202        rayon::range::Iter<T>: rayon::prelude::ParallelIterator,
203{
204    type Item = T;
205    type Iter = rayon::range::Iter<T>;
206
207    fn into_maybe_par_iter(self) -> Self::Iter {
208        rayon::range::Iter { range: self }
209    }
210}
211
212#[cfg(feature = "rayon")]
213impl<T> IntoMaybeParallelIterator for RangeInclusive<T>
214    where
215        rayon::range_inclusive::Iter<T>: rayon::prelude::ParallelIterator,
216{
217    type Item = T;
218    type Iter = rayon::range_inclusive::Iter<T>;
219
220    fn into_maybe_par_iter(self) -> Self::Iter {
221        rayon::range_inclusive::Iter { range: self }
222    }
223}
224 */
225
226#[cfg(feature = "rayon")]
227impl<IT: rayon::iter::ParallelIterator> MaybeParallelIterator<IT> {
228    pub fn collect<C>(self) -> C
229    where
230        C: rayon::iter::FromParallelIterator<IT::Item>,
231    {
232        C::from_par_iter(self)
233    }
234
235    /// Do a computation on all items.
236    pub fn for_each<O: Fn(IT::Item) + Sync + Send>(self, op: O) {
237        self.0.for_each(op)
238    }
239
240    /// Like iterator mapping.
241    pub fn map<O: Send, M: Fn(IT::Item) -> O + Send + Sync>(
242        self,
243        map: M,
244    ) -> MaybeParallelIterator<rayon::iter::Map<IT, M>> {
245        MaybeParallelIterator(self.0.map(map))
246    }
247
248    /// Like iterator filter mapping.
249    pub fn filter_map<O: Send, M: Fn(IT::Item) -> Option<O> + Send + Sync>(
250        self,
251        map: M,
252    ) -> MaybeParallelIterator<rayon::iter::FilterMap<IT, M>> {
253        MaybeParallelIterator(self.0.filter_map(map))
254    }
255
256    /// Like iterator flat mapping.
257    pub fn flat_map<O: rayon::iter::IntoParallelIterator, M: Fn(IT::Item) -> O + Send + Sync>(
258        self,
259        map: M,
260    ) -> MaybeParallelIterator<rayon::iter::FlatMap<IT, M>> {
261        MaybeParallelIterator(self.0.flat_map(map))
262    }
263
264    /// Like iterator find (but won't necessarily return the first match).
265    pub fn find_any<F: Fn(&IT::Item) -> bool + Send + Sync>(self, f: F) -> Option<IT::Item> {
266        self.0.find_any(f)
267    }
268
269    /// Get the inner parallel iterator.
270    pub fn into_inner(self) -> IT {
271        self.0
272    }
273}
274
275#[cfg(feature = "rayon")]
276impl<IT: rayon::iter::IndexedParallelIterator> MaybeParallelIterator<IT> {
277    pub fn enumerate(self) -> MaybeParallelIterator<rayon::iter::Enumerate<IT>> {
278        MaybeParallelIterator(self.0.enumerate())
279    }
280
281    /// Process at least this many items sequentially (no-op unless `rayon` feature enabled).
282    pub fn with_min_sequential(
283        self,
284        min_sequential: usize,
285    ) -> MaybeParallelIterator<rayon::iter::MinLen<IT>> {
286        MaybeParallelIterator(self.0.with_min_len(min_sequential))
287    }
288}
289
290#[cfg(feature = "rayon")]
291impl<IT: rayon::iter::ParallelIterator> rayon::iter::ParallelIterator
292    for MaybeParallelIterator<IT>
293{
294    type Item = IT::Item;
295
296    fn drive_unindexed<C>(self, consumer: C) -> C::Result
297    where
298        C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
299    {
300        self.0.drive_unindexed(consumer)
301    }
302
303    fn opt_len(&self) -> Option<usize> {
304        self.0.opt_len()
305    }
306}
307
308pub trait MaybeParallelSort<T: Send> {
309    fn maybe_par_sort(&mut self)
310    where
311        T: Ord;
312
313    fn maybe_par_sort_unstable(&mut self)
314    where
315        T: Ord;
316
317    fn maybe_par_sort_by<F>(&mut self, compare: F)
318    where
319        F: Fn(&T, &T) -> Ordering + Sync;
320
321    fn maybe_par_sort_unstable_by<F>(&mut self, compare: F)
322    where
323        F: Fn(&T, &T) -> Ordering + Sync;
324
325    fn maybe_par_sort_by_key<F, K: Ord>(&mut self, f: F)
326    where
327        F: Fn(&T) -> K + Sync;
328
329    fn maybe_par_sort_unstable_by_key<F, K: Ord>(&mut self, f: F)
330    where
331        F: Fn(&T) -> K + Sync;
332}
333
334#[cfg(not(feature = "rayon"))]
335impl<T: Send> MaybeParallelSort<T> for [T] {
336    fn maybe_par_sort(&mut self)
337    where
338        T: Ord,
339    {
340        self.sort()
341    }
342
343    fn maybe_par_sort_unstable(&mut self)
344    where
345        T: Ord,
346    {
347        self.sort_unstable()
348    }
349
350    fn maybe_par_sort_by<F>(&mut self, compare: F)
351    where
352        F: Fn(&T, &T) -> Ordering + Sync,
353    {
354        self.sort_by(compare)
355    }
356
357    fn maybe_par_sort_unstable_by<F>(&mut self, compare: F)
358    where
359        F: Fn(&T, &T) -> Ordering + Sync,
360    {
361        self.sort_unstable_by(compare)
362    }
363
364    fn maybe_par_sort_by_key<F, K: Ord>(&mut self, f: F)
365    where
366        F: Fn(&T) -> K + Sync,
367    {
368        self.sort_by_key(f)
369    }
370
371    fn maybe_par_sort_unstable_by_key<F, K: Ord>(&mut self, f: F)
372    where
373        F: Fn(&T) -> K + Sync,
374    {
375        self.sort_unstable_by_key(f)
376    }
377}
378
379#[cfg(feature = "rayon")]
380impl<T: Send, C> MaybeParallelSort<T> for C
381where
382    C: rayon::slice::ParallelSliceMut<T> + ?Sized,
383{
384    fn maybe_par_sort(&mut self)
385    where
386        T: Ord,
387    {
388        self.par_sort()
389    }
390
391    fn maybe_par_sort_unstable(&mut self)
392    where
393        T: Ord,
394    {
395        self.par_sort_unstable()
396    }
397
398    fn maybe_par_sort_by<F>(&mut self, compare: F)
399    where
400        F: Fn(&T, &T) -> Ordering + Sync,
401    {
402        self.par_sort_by(compare)
403    }
404
405    fn maybe_par_sort_unstable_by<F>(&mut self, compare: F)
406    where
407        F: Fn(&T, &T) -> Ordering + Sync,
408    {
409        self.par_sort_unstable_by(compare)
410    }
411
412    fn maybe_par_sort_by_key<F, K: Ord>(&mut self, f: F)
413    where
414        F: Fn(&T) -> K + Sync,
415    {
416        self.par_sort_by_key(f)
417    }
418
419    fn maybe_par_sort_unstable_by_key<F, K: Ord>(&mut self, f: F)
420    where
421        F: Fn(&T) -> K + Sync,
422    {
423        self.par_sort_unstable_by_key(f)
424    }
425}
426
427#[cfg(test)]
428mod tests {
429    use crate::{
430        IntoMaybeParallelIterator, IntoMaybeParallelRefIterator, IntoMaybeParallelRefMutIterator,
431        MaybeParallelSort,
432    };
433
434    #[test]
435    #[cfg(not(feature = "rayon"))]
436    fn test_sequential() {
437        let mut a: Vec<i32> = (0..100).collect();
438        a.maybe_par_iter().for_each(|item| println!("{}", item));
439        a.maybe_par_iter_mut().for_each(|item| *item -= 5);
440        println!("{:?}", a);
441        a.into_maybe_par_iter()
442            .with_min_sequential(2)
443            .map(|n| -n)
444            .enumerate()
445            .flat_map(|(e, n)| vec![e as i32, n, n + 1000].into_maybe_par_iter())
446            .for_each(|item| {
447                println!("seq: {:?}", item);
448            });
449
450        let mut to_sort: Vec<i32> = vec![5, 2, 2, 6, 1, 6];
451        to_sort.maybe_par_sort();
452        println!("{:?}", to_sort);
453
454        let mut owned = vec![1, 2, 3];
455        let slice = owned.as_slice();
456        slice.maybe_par_iter().for_each(|_| {});
457        let slice_mut = owned.as_mut_slice();
458        slice_mut.maybe_par_iter().for_each(|_| {});
459        slice_mut.maybe_par_iter_mut().for_each(|_| {});
460
461        (0..10).into_maybe_par_iter().for_each(|_| {});
462    }
463
464    #[test]
465    #[cfg(feature = "rayon")]
466    fn test_rayon() {
467        let mut a: Vec<i32> = (0..100).collect();
468        a.maybe_par_iter()
469            .with_min_sequential(5)
470            .for_each(|item| println!("{}", item));
471        a.maybe_par_iter_mut().for_each(|item| *item -= 5);
472        println!("{:?}", a);
473        a.into_maybe_par_iter()
474            .with_min_sequential(2)
475            .map(|n| -n)
476            .enumerate()
477            .flat_map(|(e, n)| vec![e as i32, n, n + 1000].into_maybe_par_iter())
478            .for_each(|item| {
479                println!("par: {:?}", item);
480            });
481
482        let mut to_sort: Vec<i32> = vec![5, 2, 2, 6, 1, 6];
483        to_sort.maybe_par_sort();
484        println!("{:?}", to_sort);
485
486        let mut owned = vec![1, 2, 3];
487        let slice = owned.as_slice();
488        slice.maybe_par_iter().for_each(|_| {});
489        let slice_mut = owned.as_mut_slice();
490        slice_mut.maybe_par_iter().for_each(|_| {});
491        slice_mut.maybe_par_iter_mut().for_each(|_| {});
492
493        (0..10).into_maybe_par_iter().for_each(|_| {});
494    }
495}