Pstd reference-counted slice

⚓ Rust    📅 2026-04-03    👤 surdeus    👁️ 5      

surdeus

I spent the last week or so writing this, made all kinds of blunders, I think I now have it correct.

The objective is a reference-counted slice (inc. string), which can be allocated not in Global, available on stable.

Here is the code. Are there still major problems with it? Any other comments? Thanks!

/// Reference-counted slice.
pub struct RcSlice<T, A: Allocator = Global> {
    nn: NonNull<RcSliceInner<A>>,
    pd: PhantomData<T>,
}

struct RcSliceInner<A: Allocator> {
    cc: usize,   // Clone count
    slen: usize, // Length of slice
    a: A,
}

impl<T, A: Allocator> RcSlice<T, A> {
    /// Create a RcSlice from s in specified allocator.
    pub fn new_in(s: &[T], a: A) -> Self
    where
        T: Clone,
    {
        unsafe {
            let slen = s.len();
            let (layout, off) = Self::layout(slen);

            let nn = a.allocate(layout).unwrap();
            let p = nn.as_ptr().cast::<RcSliceInner<A>>();

            let inner = RcSliceInner { cc: 0, slen, a };
            ptr::write(p, inner);

            // Initialise the slice of allocated memory.
            let to = (p as *mut u8).add(off) as *mut MaybeUninit<T>;
            let to = std::slice::from_raw_parts_mut(to, slen);
            to.write_clone_of_slice(s);

            Self {
                nn: NonNull::new_unchecked(p),
                pd: PhantomData,
            }
        }
    }

    /// Get the layout for the inner fields followed by a slice of size slen, and the offset of the slice.
    fn layout(slen: usize) -> (Layout, usize) {
        unsafe {
            Layout::new::<RcSliceInner<A>>()
                .extend(Layout::array::<T>(slen).unwrap_unchecked())
                .unwrap_unchecked()
        }
    }

    /// Get a NonNull pointer to the slice.
    fn slice(&self) -> NonNull<[T]> {
        unsafe {
            let p = self.nn.as_ptr();
            let slen = (*p).slen;
            let (_layout, off) = Self::layout(slen);
            let p = (p as *mut u8).add(off) as *mut T;
            let nn = NonNull::new_unchecked(p);
            NonNull::slice_from_raw_parts(nn, slen)
        }
    }
}

impl<T, A: Allocator> Clone for RcSlice<T, A> {
    fn clone(&self) -> Self {
        unsafe {
            let p = self.nn.as_ptr();
            (*p).cc += 1;
        }
        Self {
            nn: self.nn,
            pd: PhantomData,
        }
    }
}

impl<T, A: Allocator> Drop for RcSlice<T, A> {
    fn drop(&mut self) {
        unsafe {
            let p = self.nn.as_ptr();
            if (*p).cc == 0 {
                self.slice().drop_in_place();
                let slen = (*p).slen;
                let a = ptr::read(&(*p).a);
                let (layout, _off) = Self::layout(slen);
                let nn = NonNull::new_unchecked(p as *mut u8);
                a.deallocate(nn, layout)
            } else {
                (*p).cc -= 1;
            }
        }
    }
}

impl<T, A: Allocator> Deref for RcSlice<T, A> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        unsafe { self.slice().as_ref() }
    }
}

2 posts - 1 participant

Read full topic

🏷️ Rust_feed