Very quick review and some alternative methods.
Disclaimer: I have not tested any of the following code aside from verifying that it builds and I did not port your example in the README.
If your API can panic due to the user's input (and not your own bug) then it is cleaner to provide both the panicing seal
and a panic-free try_seal
, allowing the consumer more freedom in their code.
With the unsafe code, IIUC, the sealed()
slice may co-exist with an isolated mutable borrow, but it is not updated as more items are sealed. The following code does not allow immutable and mutable slices, but the alternatives after do.
impl<'a, T> SealingSlice<'a, T> {
..
pub fn seal(&mut self, n: usize) {
self.try_seal(n)
.expect("seal operation exceeds slice length")
}
pub fn try_seal(&mut self, n: usize) -> Result<(), ()> {
self.index.checked_add(n)
.filter(|n| *n <= self.slice.len())
.map(|n| self.index = n)
.ok_or(())
}
pub fn mutable(&mut self) -> &mut [T] {
&mut self.slice[..self.index]
}
pub fn sealed(&self) -> &[T] {
&self.slice[self.index..]
}
}
If you're fine with separate slices for each chunk, then this gets simpler.
use core::mem;
pub struct Buffer<'a, T> {
buf: &'a mut [T],
}
impl<'a, T> Buffer<'a, T> {
pub fn new(buf: &'a mut [T]) -> Self {
Self { buf }
}
pub fn split_mut(&mut self, n: usize) -> Option<&'a mut [T]> {
if self.buf.len() >= n {
let (l, r) = mem::replace(&mut self.buf, &mut [])
.split_at_mut(n);
self.buf = r;
Some(l)
} else {
None
}
}
pub fn split(&mut self, n: usize) -> Option<&'a [T]> {
self.split_mut(n)
.map(|x| &*x)
}
}
If you need a contiguous &[T]
for all sealed items, then you'll want to recombine them. This does require unsafe code.
use core::slice;
pub trait Coalesce: Sized {
fn coalesce(self, rhs: Self) -> Result<Self, (Self, Self)>;
}
impl<T> Coalesce for &'_ [T] {
fn coalesce(self, rhs: Self) -> Result<Self, (Self, Self)> {
unsafe {
if self.as_ptr().offset(self.len() as isize) == rhs.as_ptr() {
Ok(slice::from_raw_parts(self.as_ptr(), self.len() + rhs.len()))
} else {
Err((self, rhs))
}
}
}
}
impl<T> Coalesce for &'_ mut [T] {
fn coalesce(self, rhs: Self) -> Result<Self, (Self, Self)> {
unsafe {
if self.as_ptr().offset(self.len() as isize) == rhs.as_ptr() {
Ok(slice::from_raw_parts_mut(self.as_mut_ptr(), self.len() + rhs.len()))
} else {
Err((self, rhs))
}
}
}
}
Alternatively you can slide the index and reborrow both sides as needed without any unsafe code.
fn sealed_slice<T>(buf: &mut [T], n: usize) -> (&[T], &mut [T]) {
let (l, r) = buf.split_at_mut(n);
// convince rust the left side may be coerced to immutable
(l, r)
}