extract_if.rs - source
Description: Source of the Rust file `library/alloc/src/vec/extract_if.rs`.

#
alloc/vec/
extract_if.rs

    1use core::ops::{Range, RangeBounds};
    2use core::{fmt, ptr, slice};
    3
    4use super::Vec;
    5use crate::alloc::{Allocator, Global};
    6
    7/// An iterator which uses a closure to determine if an element should be removed.
    8///
    9/// This struct is created by [`Vec::extract_if`].
    10/// See its documentation for more.
    11///
    12/// # Example
    13///
    14/// ```
    15/// let mut v = vec![0, 1, 2];
    16/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0);
    17/// ```
    18#[stable(feature = "extract_if", since = "1.87.0")]
    19#[must_use = "iterators are lazy and do nothing unless consumed; \
    20    use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"]
    21pub struct ExtractIf<
    22    'a,
    23    T,
    24    F,
    25    #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
    26> {
    27    vec: &'a mut Vec<T, A>,
    28    /// The index of the item that will be inspected by the next call to `next`.
    29    idx: usize,
    30    /// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`.
    31    end: usize,
    32    /// The number of items that have been drained (removed) thus far.
    33    del: usize,
    34    /// The original length of `vec` prior to draining.
    35    old_len: usize,
    36    /// The filter test predicate.
    37    pred: F,
    38}
    39
    40impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> {
    41    pub(super) fn new<R: RangeBounds<usize>>(vec: &'a mut Vec<T, A>, pred: F, range: R) -> Self {
    42        let old_len = vec.len();
    43        let Range { start, end } = slice::range(range, ..old_len);
    44
    45        // Guard against the vec getting leaked (leak amplification)
    46        unsafe {
    47            vec.set_len(0);
    48        }
    49        ExtractIf { vec, idx: start, del: 0, end, old_len, pred }
    50    }
    51
    52    /// Returns a reference to the underlying allocator.
    53    #[unstable(feature = "allocator_api", issue = "32838")]
    54    #[inline]
    55    pub fn allocator(&self) -> &A {
    56        self.vec.allocator()
    57    }
    58}
    59
    60#[stable(feature = "extract_if", since = "1.87.0")]
    61impl<T, F, A: Allocator> Iterator for ExtractIf<'_, T, F, A>
    62where
    63    F: FnMut(&mut T) -> bool,
    64{
    65    type Item = T;
    66
    67    fn next(&mut self) -> Option<T> {
    68        while self.idx < self.end {
    69            let i = self.idx;
    70            // SAFETY:
    71            //  We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from
    72            //  the validity of `Self`. Therefore `i` points to an element within `vec`.
    73            //
    74            //  Additionally, the i-th element is valid because each element is visited at most once
    75            //  and it is the first time we access vec[i].
    76            //
    77            //  Note: we can't use `vec.get_unchecked_mut(i)` here since the precondition for that
    78            //  function is that i < vec.len(), but we've set vec's length to zero.
    79            let cur = unsafe { &mut *self.vec.as_mut_ptr().add(i) };
    80            let drained = (self.pred)(cur);
    81            // Update the index *after* the predicate is called. If the index
    82            // is updated prior and the predicate panics, the element at this
    83            // index would be leaked.
    84            self.idx += 1;
    85            if drained {
    86                self.del += 1;
    87                // SAFETY: We never touch this element again after returning it.
    88                return Some(unsafe { ptr::read(cur) });
    89            } else if self.del > 0 {
    90                // SAFETY: `self.del` > 0, so the hole slot must not overlap with current element.
    91                // We use copy for move, and never touch this element again.
    92                unsafe {
    93                    let hole_slot = self.vec.as_mut_ptr().add(i - self.del);
    94                    ptr::copy_nonoverlapping(cur, hole_slot, 1);
    95                }
    96            }
    97        }
    98        None
    99    }
    100
    101    fn size_hint(&self) -> (usize, Option<usize>) {
    102        (0, Some(self.end - self.idx))
    103    }
    104}
    105
    106#[stable(feature = "extract_if", since = "1.87.0")]
    107impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A> {
    108    fn drop(&mut self) {
    109        if self.del > 0 {
    110            // SAFETY: Trailing unchecked items must be valid since we never touch them.
    111            unsafe {
    112                ptr::copy(
    113                    self.vec.as_ptr().add(self.idx),
    114                    self.vec.as_mut_ptr().add(self.idx - self.del),
    115                    self.old_len - self.idx,
    116                );
    117            }
    118        }
    119        // SAFETY: After filling holes, all items are in contiguous memory.
    120        unsafe {
    121            self.vec.set_len(self.old_len - self.del);
    122        }
    123    }
    124}
    125
    126#[stable(feature = "extract_if", since = "1.87.0")]
    127impl<T, F, A> fmt::Debug for ExtractIf<'_, T, F, A>
    128where
    129    T: fmt::Debug,
    130    A: Allocator,
    131{
    132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    133        let peek = if self.idx < self.end {
    134            // This has to use pointer arithmetic as `self.vec[self.idx]` or
    135            // `self.vec.get_unchecked(self.idx)` wouldn't work since we
    136            // temporarily set the length of `self.vec` to zero.
    137            //
    138            // SAFETY:
    139            // Since `self.idx` is smaller than `self.end` and `self.end` is
    140            // smaller than `self.old_len`, `idx` is valid for indexing the
    141            // buffer. Also, per the invariant of `self.idx`, this element
    142            // has not been inspected/moved out yet.
    143            Some(unsafe { &*self.vec.as_ptr().add(self.idx) })
    144        } else {
    145            None
    146        };
    147        f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive()
    148    }
    149}