Skip to content

Rayon

Networking & ConcurrencyConcurrency/ParallelismRust

What it is

Rayon turns sequential iterator chains into parallel ones by changing iter() to par_iter(), with work-stealing and guaranteed data-race freedom.

Replace iter with par_iter on CPU-bound work over large collections. Rayon splits the work across a thread pool sized to your cores.

Installation

cargo add rayon

Getting started

The smallest useful thing you can do with it, and what each part means.

One-word parallelism
use rayon::prelude::*;

// Sequential
let total: u64 = values.iter().map(|v| expensive(*v)).sum();

// Parallel — the only change is par_iter
let total: u64 = values.par_iter().map(|v| expensive(*v)).sum();

let matches: Vec<_> = documents
    .par_iter()
    .filter(|d| d.contains(&needle))
    .cloned()
    .collect();   // order is preserved

images.par_iter_mut().for_each(|img| img.apply_filter());
If this compiles, it has no data races — the borrow checker will not permit a closure that mutably aliases shared state. Collect preserves the original order despite parallel execution.

Advanced usage

Where the library earns its place over a simpler alternative.

Custom pools and reductions
// Restrict Rayon so it does not compete with other work.
let pool = rayon::ThreadPoolBuilder::new().num_threads(4).build()?;

let result = pool.install(|| {
    data.par_iter()
        .fold(Stats::default, |mut acc, item| { acc.add(item); acc })
        .reduce(Stats::default, |a, b| a.merge(b))
});

// Parallel sort, and a fallback for small inputs.
let mut items = load();
if items.len() > 10_000 {
    items.par_sort_unstable_by_key(|i| i.score);
} else {
    items.sort_unstable_by_key(|i| i.score);
}
fold produces a per-thread accumulator and reduce merges them, which avoids the contention a shared mutex would create. Below a few thousand items the coordination overhead usually outweighs the gain.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

closure may outlive the current function
The closure borrows a local. Use par_iter on owned data, or scoped threads.
Parallel is slower than sequential
The work per item is too small, or a shared lock is serialising it. Increase the chunk of work per item or remove the contention.

Best practices

  • Use Rayon for CPU-bound work only — for I/O-bound concurrency use Tokio.
  • Measure before adopting: on small collections the overhead dominates.
  • Prefer fold/reduce over a shared Mutex, which serialises exactly what you parallelised.
  • Constrain the pool size in a server so Rayon does not starve the async runtime.

Background

Why it exists, and what it was reacting to.

Written by Niko Matsakis, Rayon is the clearest demonstration of Rust's fearless concurrency claim: the borrow checker proves the parallel version is safe, so the change really is that small.