Skip to main content

paralight/iter/sink/
mod.rs

1// Copyright 2026 The Paralight Authors
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Parallel sinks collect items from parallel iterators.
10
11#[cfg(feature = "nightly")]
12pub mod array;
13pub mod vec;
14
15/// A sink to collect items in parallel.
16///
17/// You most likely won't need to interact with this trait directly, as it is an
18/// associated type of the [`FromExactParallelSink`] trait, itself used to
19/// express output types of the
20/// [`collect()`](super::BaseExactParallelIterator::collect) and
21/// [`try_collect()`](super::BaseExactParallelIterator::try_collect) adaptors on
22/// [`BaseExactParallelIterator`](super::BaseExactParallelIterator). You can
23/// however implement this trait if you want to allow your types to be collected
24/// by these adaptors.
25pub trait ExactParallelSink {
26    /// The type of items that this parallel sink collects.
27    type Item: Send;
28
29    /// Set to [`false`] if the skip function is guaranteed to be a noop.
30    ///
31    /// Typically, skipping is a noop when [`std::mem::needs_drop()`] returns
32    /// false for the [`Item`](Self::Item) type.
33    const NEEDS_CLEANUP: bool;
34
35    /// Creates a new sink able to collect the given number of items.
36    fn new(len: usize) -> Self;
37
38    /// Pushes the given item at the given index of this sink.
39    ///
40    /// # Safety
41    ///
42    /// Given the length `len` passed to the [`new()`](Self::new) call that
43    /// initialized this sink:
44    /// - indices passed to [`push_item()`](Self::push_item) must be in the
45    ///   `0..len` range,
46    /// - each index in `0..len` must be present at most once in all indices
47    ///   passed to calls to [`push_item()`](Self::push_item) and ranges passed
48    ///   to calls to [`skip_item_range()`](Self::skip_item_range).
49    ///
50    /// It is therefore undefined behavior to call this function twice with the
51    /// same index, with an index contained in a range for which
52    /// [`skip_item_range()`](Self::skip_item_range) was invoked, etc.
53    ///
54    /// You normally shouldn't have to worry about this, because this API is
55    /// intended to be called by Paralight's internal multi-threading engine.
56    /// This API is public to allow others to implement parallel sinks: when
57    /// implementing your own sink(s), you can rely on these `unsafe`
58    /// pre-conditions.
59    unsafe fn push_item(&self, index: usize, item: Self::Item);
60
61    /// Indicates that the items in the given range won't be pushed.
62    ///
63    /// # Safety
64    ///
65    /// Given the length `len` passed to the [`new()`](Self::new) call that
66    /// initialized this sink:
67    /// - ranges passed to [`skip_item_range()`](Self::skip_item_range) must be
68    ///   included in the `0..len` range,
69    /// - each index in `0..len` must be present at most once in all indices
70    ///   passed to calls to [`push_item()`](Self::push_item) and ranges passed
71    ///   to calls to [`skip_item_range()`](Self::skip_item_range).
72    ///
73    /// It is therefore undefined behavior to call this function twice with the
74    /// same range, with overlapping ranges, with a range that contains an
75    /// index for which [`push_item()`](Self::push_item) was invoked, etc.
76    ///
77    /// You normally shouldn't have to worry about this, because this API is
78    /// intended to be called by Paralight's internal multi-threading engine.
79    /// This API is public to allow others to implement parallel sinks: when
80    /// implementing your own sink(s), you can rely on these `unsafe`
81    /// pre-conditions.
82    unsafe fn skip_item_range(&self, range: std::ops::Range<usize>);
83
84    /// Cancel and cleanup this sink.
85    ///
86    /// The purpose of this function is to properly cleanup the sink during
87    /// [unwinding](https://doc.rust-lang.org/nomicon/unwinding.html) if a panic
88    /// occured before all items have been pushed.
89    ///
90    /// # Safety
91    ///
92    /// This can only be called after all indices have been passed once and only
93    /// once to [`push_item()`](Self::push_item) and
94    /// [`skip_item_range()`](Self::skip_item_range).
95    unsafe fn cancel(self);
96}
97
98/// Trait for collecting items from an [`ExactParallelSink`].
99///
100/// You most likely won't need to interact with this trait directly, as it is
101/// only used to express output types of the
102/// [`collect()`](super::BaseExactParallelIterator::collect) and
103/// [`try_collect()`](super::BaseExactParallelIterator::try_collect) adaptors on
104/// [`BaseExactParallelIterator`](super::BaseExactParallelIterator). You can
105/// however implement this trait if you want to allow your types to be collected
106/// by these adaptors.
107pub trait FromExactParallelSink {
108    /// The type of items that this parallel sink collects.
109    type Item: Send;
110
111    /// Sink from which this type can be created.
112    type Sink: ExactParallelSink<Item = Self::Item> + Sync;
113
114    /// Converts a fully populated sink into this type.
115    ///
116    /// # Safety
117    ///
118    /// - This can only be called after all indices have been passed once and
119    ///   only once to the sink's [`push_item()`](ExactParallelSink::push_item).
120    /// - No call to [`skip_item_range()`](ExactParallelSink::skip_item_range)
121    ///   must have been made on the sink (otherwise, you need to call
122    ///   [`cancel()`](ExactParallelSink::cancel) instead).
123    unsafe fn finalize(sink: Self::Sink) -> Self;
124}