Skip to main content

ParRec

Trait ParRec 

Source
pub trait ParRec: Sized + ParRecCore {
Show 28 methods // Required methods fn runner<Q: ParRunner>( self, runner: Q, ) -> impl ParRec<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>; fn runner_with_diagnostics( self, ) -> impl ParRec<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>; fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self; fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self; fn iteration_order(self, collect: IterationOrder) -> Self; fn map<Q, H>( self, h: H, ) -> impl ParRec<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input> where H: Fn(Self::Item) -> Q + Copy + Send; fn inspect<H>( self, h: H, ) -> impl ParRec<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input> where H: Fn(&Self::Item) + Copy + Send; fn filter<H>( self, h: H, ) -> impl ParRec<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input> where H: Fn(&Self::Item) -> bool + Copy + Send; fn filter_map<Q, H>( self, h: H, ) -> impl ParRec<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input> where H: Fn(Self::Item) -> Option<Q> + Copy + Send; fn flat_map<V, H>( self, h: H, ) -> impl ParRec<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input> where V: IntoIterator, H: Fn(Self::Item) -> V + Copy + Send; fn flatten( self, ) -> impl ParRec<Item = <Self::Item as IntoIterator>::Item, Xap = FlattenOf<Self::Xap>, Input = Self::Input> where Self::Item: IntoIterator; fn first(self) -> Option<Self::Item> where Self::Item: Send, <Self::Input as IntoIterator>::Item: Send; fn reduce<F>(self, f: F) -> Option<Self::Item> where F: Fn(Self::Item, Self::Item) -> Self::Item + Send + Copy, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send; fn collect_into<P>(self, dst: &mut P) where P: ParExtend<Self::Item>, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send; fn fold<B, I, F>(self, init: I, f: F) -> Vec<B> where B: Send, I: Fn() -> B, F: Fn(&mut B, Self::Item) + Copy + Send, <Self::Input as IntoIterator>::Item: Send; // Provided methods fn collect<P>(self) -> P where P: ParExtend<Self::Item> + Default, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send { ... } fn all<F>(self, f: F) -> bool where F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn any<F>(self, f: F) -> bool where F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn count(self) -> usize where <Self::Input as IntoIterator>::Item: Send { ... } fn find<F>(self, f: F) -> Option<Self::Item> where Self::Item: Send, F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn for_each<F>(self, f: F) where F: Fn(Self::Item) + Send + Copy, <Self::Input as IntoIterator>::Item: Send { ... } fn max(self) -> Option<Self::Item> where Self::Item: Ord + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn max_by<F>(self, f: F) -> Option<Self::Item> where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn max_by_key<B, F>(self, f: F) -> Option<Self::Item> where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn min(self) -> Option<Self::Item> where Self::Item: Ord + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn min_by<F>(self, f: F) -> Option<Self::Item> where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn min_by_key<B, F>(self, f: F) -> Option<Self::Item> where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Copy + Send, <Self::Input as IntoIterator>::Item: Send { ... } fn sum<S>(self) -> S where Self::Item: Sum<S>, S: Send, <Self::Input as IntoIterator>::Item: Send { ... }
}
Expand description

Infallible parallel recursive iterator.

ParRec is the central trait for describing recursive parallel computations as iterator pipelines. It mirrors common sequential iterator operations (map, filter, flat_map, collect, reduce, …) while allowing runtime configuration of execution details such as number of threads, chunk size, iteration order, and runner/pool selection.

Recursive traversal can be deterministic: with IterationOrder::Ordered (the default), order-sensitive operations use breadth-first order, level by level and left-to-right following input and child generation order.

Related traits:

  • ParUse for worker-local mutable state,
  • ParOption for Option-based fallibility,
  • ParResult for Result-based fallibility.

§Examples

use orx_parallel::*;

// A small rooted tree represented as adjacency lists; node 0 is the root.
let children: Vec<Vec<usize>> = vec![vec![1, 2], vec![3, 4], vec![5], vec![], vec![], vec![]];

let sum_of_even_squares: usize = par_recursive([0usize], |node| children[*node].iter().copied())
    .map(|x| x * x)
    .filter(|x| x % 2 == 0)
    .sum();

assert_eq!(sum_of_even_squares, 20);

Required Methods§

Source

fn runner<Q: ParRunner>( self, runner: Q, ) -> impl ParRec<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>

Replaces the current parallel runner with runner.

This allows per-computation control over execution strategy.

Please see Runner for parallel runners implemented in this crate.

§Examples
use orx_parallel::*;

let children: Vec<Vec<usize>> = vec![vec![1, 2], vec![3, 4], vec![5], vec![], vec![], vec![]];

let baseline: usize = par_recursive([0usize], |node| children[*node].iter().copied()).sum();

let par = par_recursive([0usize], |node| children[*node].iter().copied());

let par = par.runner(Runner::fixed());

let configured: usize = par.sum();
assert_eq!(baseline, configured);
Source

fn runner_with_diagnostics( self, ) -> impl ParRec<Item = Self::Item, Xap = Self::Xap, Input = Self::Input>

Wraps the current parallel runner with a diagnostics-enabled runner.

The returned iterator behaves the same, but additionally reports runtime diagnostics at the end of the computation.

§Examples
use orx_parallel::*;

let par = par_recursive([1i32], |&x| (x < 10_000).then_some(x + 1))
    .num_threads(4);

#[cfg(feature = "std")]
let par = par.runner_with_diagnostics();

let sum = par.sum::<i32>();
assert_eq!(sum, 50005000);

This will print a summary report which currently looks like the following:

│ # Parallel Executor Diagnostics
│
│   Available threads : 4
│   Used threads      : 4
│   Wall time         : 1.15 ms
│
│ ## Summary Table
│   thread  num_chunks   num_tasks  min_chunk  avg_chunk  max_chunk    util%
│   ------  ----------  ----------  ---------  ---------  ---------  -------
│        0          35       27335        781        781        781   100.0%
│        1          32       24992        781        781        781    91.5%
│        2          30       23430        781        781        781    85.9%
│        3          28       21868        781        781        781    77.8%
│
│ ## Workload Balance
│   max/min task ratio  : 1.25x  (1.00 = perfect balance)
│   coeff. of variation : 8.3%  (lower is better)
│
│ ## Thread Active Timeline  (each block ≈ 0.02 ms)
│   [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│   [ 1]     ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│   [ 2]         ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│   [ 3]             ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
│
│ ## Thread Task Distribution  (bar length ∝ tasks processed)
│   [ 0] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇  (27335)
│   [ 1] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇  (24992)
│   [ 2] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇  (23430)
│   [ 3] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇  (21868)
Source

fn num_threads(self, num_threads: impl Into<NumThreads>) -> Self

Sets the maximum number of worker threads for this computation.

This method configures the computation layer of the thread count decision. The actual number of threads used is determined by combining:

  1. Pool constraint (from pool() method or default pool)
    • Already includes ORX_NUM_THREADS environment variable constraint
  2. Computation constraint (this method)
    • Your per-computation thread preference
  3. Input size constraint
    • Cannot spawn more threads than input elements

The actual thread count is the minimum of all these constraints.

§Parameter Interpretation

Integer values map as follows:

  • 0 => NumThreads::Auto (use all available threads, spawn only as needed)
  • n > 0 => NumThreads::Max(n) (cap at n threads)
§Thread Count Decision Logic
available = pool.max_num_threads()      // Pool maximum (includes env variable)

requested = match num_threads {
    0 | Auto => input_size.max(1),      // Limited by input size
    Max(n) => min(input_size, n),       // Limited by input size and this param
};

actual_threads = min(requested, available)
§Examples
ⓘ
use orx_parallel::*;

// Sequential execution
let sum: usize = par_recursive([1usize], |&x| (x < 10).then_some(x + 1))
    .num_threads(1)
    .sum();
assert_eq!(sum, 55);

// Cap at 4 threads
let sum: usize = par_recursive([1usize], |&x| (x < 1000).then_some(x + 1))
    .num_threads(4)
    .sum();

// Auto: uses available threads (respects ORX_NUM_THREADS)
let sum: usize = par_recursive([1usize], |&x| (x < 10).then_some(x + 1))
    .num_threads(0)
    .sum();
§See Also
Source

fn chunk_size(self, chunk_size: impl Into<ChunkSize>) -> Self

Sets chunk size used when pulling items from the concurrent input.

Integer values map as follows:

  • 0 => automatic (default)
  • n > 0 => exact chunk size n
§Examples
use orx_parallel::*;

let values: Vec<_> = par_recursive([0usize], |&x| (x < 31).then_some(x + 1))
    .chunk_size(8)
    .map(|x| x + 1)
    .collect();

assert_eq!(values.len(), 32);
assert_eq!(values[0], 1);
assert_eq!(values[31], 32);
§Rules of Thumb
  • Automatic chunk size (default) is efficient in general. Parallel runner aims to find best chunk sizes to balance between minimizing parallelization overhead and maximizing resource utilization.
  • While tuning a specific computation, we aim to find the smallest chunk size that is large enough to mitigate the impact of parallelization overhead.
  • If the individual tasks are large enough, parallelization overhead becomes insignificant making chunk_size = 1 the optimal choice.
Source

fn iteration_order(self, collect: IterationOrder) -> Self

Sets iteration order semantics for operations sensitive to ordering.

Ordered (default) preserves positional meaning (for example, first returns the earliest matching element in input order). Arbitrary allows any matching element that is reached first in parallel execution.

§Examples
use orx_parallel::*;

let ordered = par_recursive([1i32], |&x| (x < 9_999).then_some(x + 1))
    .iteration_order(IterationOrder::Ordered)
    .find(|x| x % 3421 == 0);
assert_eq!(ordered, Some(3421));

let any = par_recursive([1i32], |&x| (x < 9_999).then_some(x + 1))
    .iteration_order(IterationOrder::Arbitrary)
    .find(|x| x % 3421 == 0)
    .unwrap();
assert!([3421, 6842].contains(&any));
Source

fn map<Q, H>( self, h: H, ) -> impl ParRec<Item = Q, Xap = MapOf<Self::Xap, Q, H>, Input = Self::Input>
where H: Fn(Self::Item) -> Q + Copy + Send,

Maps each element with closure h.

§Examples
use orx_parallel::*;

let doubled: Vec<_> = par_recursive([1i32], |&x| (x < 3).then_some(x + 1))
    .map(|x| 2 * x)
    .collect();
assert_eq!(doubled, vec![2, 4, 6]);
Source

fn inspect<H>( self, h: H, ) -> impl ParRec<Item = Self::Item, Xap = InsOf<Self::Xap, H>, Input = Self::Input>
where H: Fn(&Self::Item) + Copy + Send,

Runs h on each element and forwards the item unchanged.

Useful for logging or debugging pipelines.

§Examples
use orx_parallel::*;

let out: Vec<_> = par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .inspect(|x| {
        println!("observed {x}");
    })
    .collect();

assert_eq!(out, vec![1, 2, 3, 4]);
Source

fn filter<H>( self, h: H, ) -> impl ParRec<Item = Self::Item, Xap = FilOf<Self::Xap, H>, Input = Self::Input>
where H: Fn(&Self::Item) -> bool + Copy + Send,

Keeps only elements satisfying predicate h.

§Examples
use orx_parallel::*;

let odds: Vec<_> = par_recursive([1i32], |&x| (x < 6).then_some(x + 1))
    .filter(|x| x % 2 == 1)
    .collect();
assert_eq!(odds, vec![1, 3, 5]);
Source

fn filter_map<Q, H>( self, h: H, ) -> impl ParRec<Item = Q, Xap = FilMapOf<Self::Xap, Q, H>, Input = Self::Input>
where H: Fn(Self::Item) -> Option<Q> + Copy + Send,

Maps and filters in a single pass.

Returns mapped values for elements where h returns Some(_).

§Examples
use orx_parallel::*;

let numbers: Vec<_> = par_recursive(["1", "x", "5"], |_: &&str| None::<&str>)
    .filter_map(|s| s.parse::<usize>().ok())
    .collect();

assert_eq!(numbers, vec![1, 5]);
Source

fn flat_map<V, H>( self, h: H, ) -> impl ParRec<Item = V::Item, Xap = FlatMapOf<Self::Xap, V, H>, Input = Self::Input>
where V: IntoIterator, H: Fn(Self::Item) -> V + Copy + Send,

Maps each element to an iterator and flattens one level.

§Examples
use orx_parallel::*;

let out: Vec<_> = par_recursive([1i32], |&x| (x < 3).then_some(x + 1))
    .flat_map(|x| [x, x + 10])
    .collect();
assert_eq!(out, vec![1, 11, 2, 12, 3, 13]);
Source

fn flatten( self, ) -> impl ParRec<Item = <Self::Item as IntoIterator>::Item, Xap = FlattenOf<Self::Xap>, Input = Self::Input>
where Self::Item: IntoIterator,

Flattens one level of nested iterables.

§Examples
use orx_parallel::*;

let nested = vec![vec![1, 2], vec![3, 4]];
let mut flat: Vec<_> = par_recursive(nested, |_: &Vec<i32>| None::<Vec<i32>>)
    .flatten()
    .collect();
flat.sort();

assert_eq!(flat, vec![1, 2, 3, 4]);
Source

fn first(self) -> Option<Self::Item>
where Self::Item: Send, <Self::Input as IntoIterator>::Item: Send,

Returns an item, or None if empty.

When IterationOrder::Ordered (default) is set, returns the first item in deterministic breadth-first order (level by level, left-to-right following input and child generation order).

Setting IterationOrder::Arbitrary may provide speed improvements when ordering is not important; however, ordered traversal is also optimized so the performance difference is generally small.

This operation is short-circuiting: once a first candidate is determined, remaining work is cancelled.

§Examples
use orx_parallel::*;

let empty = par_recursive(Vec::<usize>::new(), |_: &usize| None::<usize>).first();
assert_eq!(empty, None);

let first = par_recursive([1usize], |&x| (x < 3).then_some(x + 1))
    .first();
assert_eq!(first, Some(1));
Source

fn reduce<F>(self, f: F) -> Option<Self::Item>
where F: Fn(Self::Item, Self::Item) -> Self::Item + Send + Copy, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send,

Reduces items into one value using associative reducer f.

Returns None for an empty iterator.

§Examples
use orx_parallel::*;

let reduced = par_recursive([1i32], |&x| (x < 5).then_some(x + 1))
    .reduce(|a, b| a + b);
assert_eq!(reduced, Some(15));
Source

fn collect_into<P>(self, dst: &mut P)
where P: ParExtend<Self::Item>, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send,

Collects all items into dst.

When IterationOrder::Ordered (default) is set, items are collected in a deterministic breadth-first order (level by level, left-to-right following input and child generation order).

Setting IterationOrder::Arbitrary may provide speed improvements when ordering is not important; however, ordered collection is also optimized so the performance difference is generally small.

§Examples
use orx_parallel::*;

let mut dst = vec![10];
par_recursive([0i32], |&x| (x < 2).then_some(x + 1))
    .collect_into(&mut dst);
assert_eq!(dst, vec![10, 0, 1, 2]);
Source

fn fold<B, I, F>(self, init: I, f: F) -> Vec<B>
where B: Send, I: Fn() -> B, F: Fn(&mut B, Self::Item) + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Folds elements into per-thread accumulators and returns them.

The output contains one accumulator for each participating worker.

§Examples
use orx_parallel::*;

let partials: Vec<usize> = par_recursive([1usize], |&x| (x < 5).then_some(x + 1))
    .num_threads(2)
    .fold(|| 0usize, |acc, x| *acc += x);

assert!(!partials.is_empty());

assert_eq!(partials.iter().sum::<usize>(), 15);

Provided Methods§

Source

fn collect<P>(self) -> P
where P: ParExtend<Self::Item> + Default, Self::Item: Send, <Self::Input as IntoIterator>::Item: Send,

Collects all items into a new collection.

When IterationOrder::Ordered (default) is set, items are collected in a deterministic breadth-first order (level by level, left-to-right following input and child generation order).

Setting IterationOrder::Arbitrary may provide speed improvements when ordering is not important; however, ordered collection is also optimized so the performance difference is generally small.

§Examples
use orx_parallel::*;

let out: Vec<_> = par_recursive([1i32], |&x| (x < 3).then_some(x + 1))
    .map(|x| x * 2)
    .collect();
assert_eq!(out, vec![2, 4, 6]);
Source

fn all<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns true if all items satisfy predicate f.

Empty iterators return true.

This operation is short-circuiting: evaluation stops as soon as one item fails the predicate.

§Examples
use orx_parallel::*;

assert!(par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .all(|x| x > &0));
assert!(!par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .all(|x| x % 2 == 0));
Source

fn any<F>(self, f: F) -> bool
where F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns true if any item satisfies predicate f.

Empty iterators return false.

This operation is short-circuiting: evaluation stops as soon as one item satisfies the predicate.

§Examples
use orx_parallel::*;

assert!(par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .any(|x| x % 2 == 0));
assert!(!par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .any(|x| x > &10));
Source

fn count(self) -> usize
where <Self::Input as IntoIterator>::Item: Send,

Counts elements.

§Examples
use orx_parallel::*;

let n = par_recursive([1i32], |&x| (x < 10).then_some(x + 1))
    .filter(|x| x % 3 == 0)
    .count();
assert_eq!(n, 3);
Source

fn find<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item) -> bool + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Finds the first item satisfying predicate f, or None if none match.

When IterationOrder::Ordered (default) is set, returns the first matching item in deterministic breadth-first order (level by level, left-to-right following input and child generation order).

Setting IterationOrder::Arbitrary may provide speed improvements when ordering is not important; however, ordered traversal is also optimized so the performance difference is generally small.

This is equivalent to self.filter(f).first().

This operation is short-circuiting: once a matching item is found, remaining work is cancelled.

§Examples
use orx_parallel::*;

let found = par_recursive([1i32], |&x| (x < 100).then_some(x + 1))
    .find(|x| x % 17 == 0);
assert_eq!(found, Some(17));
Source

fn for_each<F>(self, f: F)
where F: Fn(Self::Item) + Send + Copy, <Self::Input as IntoIterator>::Item: Send,

Executes f for each item.

§Examples
use core::sync::atomic::{AtomicUsize, Ordering};
use orx_parallel::*;

let total = AtomicUsize::new(0);

par_recursive([1usize], |&x| (x < 4).then_some(x + 1))
    .for_each(|x| {
        total.fetch_add(x, Ordering::Relaxed);
    });

assert_eq!(total.load(Ordering::Relaxed), 10);
Source

fn max(self) -> Option<Self::Item>
where Self::Item: Ord + Send, <Self::Input as IntoIterator>::Item: Send,

Returns maximum element, or None if empty.

§Examples
use orx_parallel::*;

let max = par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .max();
assert_eq!(max, Some(4));

let empty = par_recursive(Vec::<usize>::new(), |_: &usize| None::<usize>)
    .max();
assert_eq!(empty, None);
Source

fn max_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns element considered maximum by comparator f.

§Examples
use orx_parallel::*;

let x = par_recursive(vec![-3_i32, 0, 1, 5, -10], |_: &i32| None::<i32>)
    .max_by(|a, b| a.cmp(b));
assert_eq!(x, Some(5));
Source

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns element with maximum key value.

§Examples
use orx_parallel::*;

let x = par_recursive(vec![-3_i32, 0, 1, 5, -10], |_: &i32| None::<i32>)
    .max_by_key(|x| x.abs());
assert_eq!(x, Some(-10));
Source

fn min(self) -> Option<Self::Item>
where Self::Item: Ord + Send, <Self::Input as IntoIterator>::Item: Send,

Returns minimum element, or None if empty.

§Examples
use orx_parallel::*;

let min = par_recursive([1i32], |&x| (x < 4).then_some(x + 1))
    .min();
assert_eq!(min, Some(1));

let empty = par_recursive(Vec::<usize>::new(), |_: &usize| None::<usize>)
    .min();
assert_eq!(empty, None);
Source

fn min_by<F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, F: Fn(&Self::Item, &Self::Item) -> Ordering + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns element considered minimum by comparator f.

§Examples
use orx_parallel::*;

let x = par_recursive(vec![-3_i32, 0, 1, 5, -10], |_: &i32| None::<i32>)
    .min_by(|a, b| a.cmp(b));
assert_eq!(x, Some(-10));
Source

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where Self::Item: Send, B: Ord, F: Fn(&Self::Item) -> B + Copy + Send, <Self::Input as IntoIterator>::Item: Send,

Returns element with minimum key value.

§Examples
use orx_parallel::*;

let x = par_recursive(vec![-3_i32, 0, 1, 5, -10], |_: &i32| None::<i32>)
    .min_by_key(|x| x.abs());
assert_eq!(x, Some(0));
Source

fn sum<S>(self) -> S
where Self::Item: Sum<S>, S: Send, <Self::Input as IntoIterator>::Item: Send,

Sums elements using Sum implementation of the item type.

Empty iterators return additive identity (zero).

§Examples
use orx_parallel::*;

let sum: usize = par_recursive([1usize], |&x| (x < 4).then_some(x + 1))
    .sum();
assert_eq!(sum, 10);

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<I, X, Ix, Ex, R> ParRec for ParRecIter<I, X, Ix, Ex, R>
where I: IntoIterator, X: Xap<I = I::Item>, R: ParRunner, Ix: IntoIterator<Item = X::I>, Ex: Fn(&I::Item) -> Ix + Send + Copy,