From 5b41490056a5c88b161b2f6ac8c19a5bfcb11f46 Mon Sep 17 00:00:00 2001 From: Tristan Guichaoua Date: Sun, 19 Mar 2023 10:45:29 +0100 Subject: [PATCH 1/3] Punctuated::drain --- src/punctuated.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/src/punctuated.rs b/src/punctuated.rs index 7880eb29ca..009ccca6be 100644 --- a/src/punctuated.rs +++ b/src/punctuated.rs @@ -26,7 +26,8 @@ use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; #[cfg(any(feature = "full", feature = "derive"))] use std::iter; -use std::ops::{Index, IndexMut}; +use std::iter::FusedIterator; +use std::ops::{Index, IndexMut, RangeBounds}; use std::option; use std::slice; use std::vec; @@ -139,6 +140,73 @@ impl Punctuated { } } + /// Removes the specified range from the punctuated in bulk, returning all + /// removed elements as an iterator. If the iterator is dropped before + /// being fully consumed, it drops the remaining removed elements. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the punctuated. + /// + /// # Leaking + /// + /// If the returned iterator goes out of scope without being dropped (due to + /// [`mem::forget`], for example), the punctuated may have lost and leaked + /// elements arbitrarily, including elements outside the range. + pub fn drain(&mut self, range: R) -> Drain + where + R: RangeBounds, + { + let len = self.len(); + + // FIXME: use std::slice::range when stabilized + // let mut range = std::slice::range(range, ..len); + let mut range = { + use std::ops::{Bound, Range}; + + let start = range.start_bound(); + let start = match start { + Bound::Included(&start) => start, + Bound::Excluded(start) => start + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice from after maximum usize")), + Bound::Unbounded => 0, + }; + + let end = range.end_bound(); + let end = match end { + Bound::Included(end) => end + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice up to maximum usize")), + Bound::Excluded(&end) => end, + Bound::Unbounded => len, + }; + + if start > end { + panic!("slice index starts at {start} but ends at {end}"); + } + if end > len { + panic!("range end index {end} out of range for slice of length {len}"); + } + + Range { start, end } + }; + + let last; + + if range.end == len && self.last.is_some() { + last = self.last.take(); + range.end -= 1; + } else { + last = None; + } + + let inner = self.inner.drain(range); + + Drain { inner, last } + } + /// Appends a syntax tree node onto the end of this punctuated sequence. The /// sequence must already have a trailing punctuation, or be empty. /// @@ -901,6 +969,45 @@ where { } +/// A draining iterator for `Punctuated`. +/// +/// This `struct` is created by [`Punctuated::drain`]. +/// See its documentation for more. +pub struct Drain<'a, T, P> { + inner: vec::Drain<'a, (T, P)>, + last: Option>, +} + +impl<'a, T, P> Iterator for Drain<'a, T, P> { + type Item = Pair; + + fn next(&mut self) -> Option { + self.inner + .next() + .map(|(t, p)| Pair::Punctuated(t, p)) + .or_else(|| self.last.take().map(|t| Pair::End(*t))) + } + + fn size_hint(&self) -> (usize, Option) { + // Note: vector's maximum capacity is `isize::MAX`, so this sum will never overflow + let len = self.inner.len() + (if self.last.is_some() { 1 } else { 0 }); + (len, Some(len)) + } +} + +impl<'a, T, P> DoubleEndedIterator for Drain<'a, T, P> { + fn next_back(&mut self) -> Option { + self.last + .take() + .map(|t| Pair::End(*t)) + .or_else(|| self.inner.next_back().map(|(t, p)| Pair::Punctuated(t, p))) + } +} + +impl<'a, T, P> ExactSizeIterator for Drain<'a, T, P> {} + +impl<'a, T, P> FusedIterator for Drain<'a, T, P> {} + /// A single syntax tree node of type `T` followed by its trailing punctuation /// of type `P` if any. /// From 3fd55927ae87ad8c80aa6950f607ba8894f7f7f6 Mon Sep 17 00:00:00 2001 From: Tristan Guichaoua Date: Sun, 19 Mar 2023 10:53:10 +0100 Subject: [PATCH 2/3] fix doc --- src/punctuated.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/punctuated.rs b/src/punctuated.rs index 009ccca6be..fb05040304 100644 --- a/src/punctuated.rs +++ b/src/punctuated.rs @@ -152,7 +152,7 @@ impl Punctuated { /// # Leaking /// /// If the returned iterator goes out of scope without being dropped (due to - /// [`mem::forget`], for example), the punctuated may have lost and leaked + /// [`std::mem::forget`], for example), the punctuated may have lost and leaked /// elements arbitrarily, including elements outside the range. pub fn drain(&mut self, range: R) -> Drain where From 7099932b67e5573f634eda153472d5b317bc8a7a Mon Sep 17 00:00:00 2001 From: Tristan Guichaoua Date: Sun, 19 Mar 2023 10:54:20 +0100 Subject: [PATCH 3/3] fix build check --- src/punctuated.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/punctuated.rs b/src/punctuated.rs index fb05040304..f20a5eeacb 100644 --- a/src/punctuated.rs +++ b/src/punctuated.rs @@ -184,10 +184,18 @@ impl Punctuated { }; if start > end { - panic!("slice index starts at {start} but ends at {end}"); + panic!( + "slice index starts at {start} but ends at {end}", + start = start, + end = end + ); } if end > len { - panic!("range end index {end} out of range for slice of length {len}"); + panic!( + "range end index {end} out of range for slice of length {len}", + end = end, + len = len + ); } Range { start, end }