Genericise drain function over storage

This commit is contained in:
Sosthène Guédon 2025-03-21 11:16:10 +01:00
parent 7759201dd4
commit 3e7c9aa7fe
No known key found for this signature in database
GPG Key ID: 36DA48A4C827B354
2 changed files with 27 additions and 112 deletions

View File

@ -68,71 +68,6 @@ pub type String<const N: usize> = StringInner<OwnedVecStorage<u8, N>>;
/// A dynamic capacity [`String`](https://doc.rust-lang.org/std/string/struct.String.html).
pub type StringView = StringInner<ViewVecStorage<u8>>;
impl StringView {
/// Removes the specified range from the string in bulk, returning all
/// removed characters as an iterator.
///
/// The returned iterator keeps a mutable borrow on the string to optimize
/// its implementation.
///
/// # Panics
///
/// Panics if the starting point or end point do not lie on a [`char`]
/// boundary, or if they're out of bounds.
///
/// # Leaking
///
/// If the returned iterator goes out of scope without being dropped (due to
/// [`core::mem::forget`], for example), the string may still contain a copy
/// of any drained characters, or may have lost characters arbitrarily,
/// including characters outside the range.
///
/// # Examples
///
/// ```
/// use heapless::String;
///
/// let mut s = String::<32>::try_from("α is alpha, β is beta").unwrap();
/// let beta_offset = s.find('β').unwrap_or(s.len());
///
/// // Remove the range up until the β from the string
/// let t: String<32> = s.drain(..beta_offset).collect();
/// assert_eq!(t, "α is alpha, ");
/// assert_eq!(s, "β is beta");
///
/// // A full range clears the string, like `clear()` does
/// s.drain(..);
/// assert_eq!(s, "");
/// ```
pub fn drain<R>(&mut self, range: R) -> Drain<'_>
where
R: RangeBounds<usize>,
{
// Memory safety
//
// The `String` version of `Drain` does not have the memory safety issues
// of the `Vec` version. The data is just plain bytes.
// Because the range removal happens in `Drop`, if the `Drain` iterator is leaked,
// the removal will not happen.
let Range { start, end } = crate::slice::range(range, ..self.len());
assert!(self.is_char_boundary(start));
assert!(self.is_char_boundary(end));
// Take out two simultaneous borrows. The &mut String won't be accessed
// until iteration is over, in Drop.
let self_ptr = self as *mut _;
// SAFETY: `slice::range` and `is_char_boundary` do the appropriate bounds checks.
let chars_iter = unsafe { self.get_unchecked(start..end) }.chars();
Drain {
start,
end,
iter: chars_iter,
string: self_ptr,
}
}
}
impl<const N: usize> String<N> {
/// Constructs a new, empty `String` with a fixed capacity of `N` bytes.
///
@ -275,7 +210,9 @@ impl<const N: usize> String<N> {
pub fn into_bytes(self) -> Vec<u8, N> {
self.vec
}
}
impl<S: VecStorage<u8> + ?Sized> StringInner<S> {
/// Removes the specified range from the string in bulk, returning all
/// removed characters as an iterator.
///
@ -315,11 +252,30 @@ impl<const N: usize> String<N> {
where
R: RangeBounds<usize>,
{
self.as_mut_view().drain(range)
}
}
// Memory safety
//
// The `String` version of `Drain` does not have the memory safety issues
// of the `Vec` version. The data is just plain bytes.
// Because the range removal happens in `Drop`, if the `Drain` iterator is leaked,
// the removal will not happen.
let Range { start, end } = crate::slice::range(range, ..self.len());
assert!(self.is_char_boundary(start));
assert!(self.is_char_boundary(end));
// Take out two simultaneous borrows. The &mut String won't be accessed
// until iteration is over, in Drop.
let self_ptr = self.as_mut_view() as *mut _;
// SAFETY: `slice::range` and `is_char_boundary` do the appropriate bounds checks.
let chars_iter = unsafe { self.get_unchecked(start..end) }.chars();
Drain {
start,
end,
iter: chars_iter,
string: self_ptr,
}
}
impl<S: VecStorage<u8> + ?Sized> StringInner<S> {
/// Get a reference to the `String`, erasing the `N` const-generic.
///
///

View File

@ -479,48 +479,9 @@ impl<T, const N: usize> Vec<T, N> {
}
new
}
/// Removes the specified range from the vector in bulk, returning all
/// removed elements as an iterator. If the iterator is dropped before
/// being fully consumed, it drops the remaining removed elements.
///
/// The returned iterator keeps a mutable borrow on the vector to optimize
/// its implementation.
///
/// # Panics
///
/// Panics if the starting point is greater than the end point or if
/// the end point is greater than the length of the vector.
///
/// # Leaking
///
/// If the returned iterator goes out of scope without being dropped (due to
/// [`mem::forget`], for example), the vector may have lost and leaked
/// elements arbitrarily, including elements outside the range.
///
/// # Examples
///
/// ```
/// use heapless::Vec;
///
/// let mut v = Vec::<_, 8>::from_array([1, 2, 3]);
/// let u: Vec<_, 8> = v.drain(1..).collect();
/// assert_eq!(v, &[1]);
/// assert_eq!(u, &[2, 3]);
///
/// // A full range clears the vector, like `clear()` does.
/// v.drain(..);
/// assert_eq!(v, &[]);
/// ```
pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
where
R: RangeBounds<usize>,
{
self.as_mut_view().drain(range)
}
}
impl<T> VecView<T> {
impl<T, S: VecStorage<T> + ?Sized> VecInner<T, S> {
/// Removes the specified range from the vector in bulk, returning all
/// removed elements as an iterator. If the iterator is dropped before
/// being fully consumed, it drops the remaining removed elements.
@ -573,7 +534,7 @@ impl<T> VecView<T> {
unsafe {
// Set `self.vec` length's to `start`, to be safe in case `Drain` is leaked.
self.set_len(start);
let vec = NonNull::from(self);
let vec = NonNull::from(self.as_mut_view());
let range_slice = slice::from_raw_parts(vec.as_ref().as_ptr().add(start), end - start);
Drain {
tail_start: end,
@ -583,9 +544,7 @@ impl<T> VecView<T> {
}
}
}
}
impl<T, S: VecStorage<T> + ?Sized> VecInner<T, S> {
/// Get a reference to the `Vec`, erasing the `N` const-generic.
///
///