Data Parallelism with Rayon
Rayon turns sequential iterators into parallel ones with a work-stealing thread pool. It shines on CPU-bound data processing without manually managing threads.
Search across all documentation pages
Rayon turns sequential iterators into parallel ones with a work-stealing thread pool. It shines on CPU-bound data processing without manually managing threads.
Quick-reference recipe card - copy-paste ready.
use rayon::prelude::*;
fn main() {
let sum: i32 = (1..=1_000_000).into_par_iter().sum();
println!("{sum}");
}When to reach for this: Parallel maps, filters, reductions, and sorting over in-memory collections.
use rayon::prelude::*;
#[derive(Debug)]
struct Record {
id: u64,
value: f64,
}
fn main() {
let records: Vec<Record> = (0..100_000)
.map(|i| Record {
id: i,
value: (i as f64).sqrt(),
})
.collect();
let top: Vec<_> = records
.par_iter()
.filter(|r| r.value > 100.0)
.map(|r| r.id)
.collect();
let total: f64 = records.par_iter().map(|r| r.value).sum();
println!("matches = {}, total = {:.2}", top.len(), total);
}What this demonstrates:
par_iter splits work across Rayon's pool.sum.par_iter splits ranges until chunks are small enough.Send - same as thread::spawn.| Method | Purpose |
|---|---|
par_iter() | Parallel immutable iteration |
par_iter_mut() | Parallel in-place mutation |
into_par_iter() | Consume collection in parallel |
par_sort / par_sort_unstable | Parallel sort on slices |
// Custom thread pool for isolation
use rayon::ThreadPoolBuilder;
let pool = ThreadPoolBuilder::new().num_threads(4).build().unwrap();
pool.install(|| {
let result = data.par_iter().map(expensive).sum::<i32>();
});pool.install to scope custom pools.par_iter closures - blocks pool threads.ParallelIterator requires Send for T and closures.spawn_blocking).sum on floats or custom ops may vary by order. Fix: Use sequential reduce or compensated summation.map to produce values then reduce, or fold per partition.Rc across par_iter - Rc is not Send. Fix: Use Arc or process owned data per item.| Alternative | Use When | Don't Use When |
|---|---|---|
thread::scope | Manual chunking with borrows | You want iterator ergonomics |
| Sequential iterators | Small data or cheap per-item work | CPU-bound over large collections |
tokio::task::spawn_blocking | CPU work inside async apps | Pure sync batch jobs |
| SIMD / GPU crates | Numeric kernels at extreme scale | General business logic maps |
CPU-bound work over large in-memory data with independent items - parsing, transforms, analytics.
collect order matches source order. reduce may use different tree order - use associative ops.
Call Rayon from spawn_blocking in Tokio - never block the async executor on par_iter directly in hot paths.
ThreadPoolBuilder::num_threads(n) - useful to leave cores for other services.
Mutate elements in parallel when each index is independent - no aliasing violations.
Rayon detects nested par_iter and avoids oversubscription in many cases - still profile.
Collect Result with try_reduce or map to Result then short-circuit with custom fold.
par_sort_unstable is fast on large slices - ensure Ord is cheap.
Polars uses its own parallel engine - use Rayon for custom Rust pipelines outside Polars.
RAYON_NUM_THREADS=1 forces sequential behavior to reproduce race-free logic bugs.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 16, 2026