Skip to content

Instantly share code, notes, and snippets.

@Techcable
Created August 24, 2025 08:12
Show Gist options
  • Save Techcable/a06f74db0f62cf31521cf917ddaae78d to your computer and use it in GitHub Desktop.
Save Techcable/a06f74db0f62cf31521cf917ddaae78d to your computer and use it in GitHub Desktop.
[DRAFT] An implementation of the stdlib-internal `RawVec` type using the `Vec` for the allocation
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
/// Check if growth is necessary.
#[inline]
pub fn needs_to_grow(capacity: usize, len: usize, additional: usize) -> bool {
// https://github.com/rust-lang/rust/blob/1.89.0/library/alloc/src/raw_vec/mod.rs#L626-L628
additional > capacity.wrapping_sub(len)
}
/// Similar to the stdlib-internal [`RawVec`] type,
/// but using a [`Vec`] for the underlying allocation logic
/// instead of reimplementing from scratch.
///
/// This is perfectly safe due to the guarantees made by the [`Vec`] type.
///
/// [`RawVec`]: https://github.com/rust-lang/rust/blob/1.89.0/library/alloc/src/raw_vec/mod.rs#L51-L74
pub struct RawVec<T> {
/// Pointer to the allocated memory, or dangling if `capacity == 0`.
///
/// The use of [`ManuallyDrop`] means the destructor of `T` can be statically known to be unnecessary,
/// hopefully avoiding some overhead when converted back into a `Vec` during drop.
ptr: NonNull<ManuallyDrop<T>>,
capacity: usize,
marker: PhantomData<T>,
}
impl<T> Default for RawVec<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> RawVec<T> {
/// If conversion to/from a `Vec` needs an extra check for `capacity == 0`,
/// to avoid calling `Vec::from_raw_parts` on a dangling pointer.
///
/// Technically, this is undefined behavior, so we avoid it.
/// On newer versions we can initialize to `const { Vec::new().as_ptr() }` instead of a
/// dangling pointer, avoiding the check entirely.
const NEED_DANGLING_CHECK: bool = rustversion::cfg!(before(1.87));
#[inline]
#[rustversion::since(1.87)]
pub const fn new() -> Self {
const { assert!(!Self::NEED_DANGLING_CHECK) }
Self::from_vec(Vec::new())
}
#[inline]
#[rustversion::before(1.87)]
pub const fn new() -> Self {
const {
assert!(Self::NEED_DANGLING_CHECK);
}
RawVec {
ptr: NonNull::dangling(),
capacity: 0,
marker: PhantomData,
}
}
/// Grow the vector to meet the specified capacity if it is necessary to do so.
#[inline]
pub fn grow(&mut self, required_capacity: usize) {
if required_capacity > self.capacity {
self.grow_slow(required_capacity)
}
}
/// Cold-path function to actually grow the vector.
#[cold]
pub fn grow_slow(&mut self, required_capacity: usize) {
// SAFETY: Not dropped
let mut vec = unsafe { self.as_vec_unchecked() };
debug_assert_eq!(vec.len(), 0);
vec.reserve(required_capacity);
debug_assert!(vec.capacity() >= required_capacity);
debug_assert!(vec.capacity() >= self.capacity);
self.capacity = vec.capacity();
}
/// Convert a `Vec<ManuallyDrop<T>>` into a `RawVec`.
///
/// The elements themselves will be leaked,
/// but this is fine since
#[rustversion::attr(since(1.87), const)] // const Vec::as_ptr
#[rustversion::attr(before(1.87), allow(dead_code))]
#[inline]
const fn from_vec(mut vec: Vec<ManuallyDrop<T>>) -> Self {
debug_assert!(vec.len() == 0);
// SAFETY: A vector's pointer is never null
let ptr = unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) };
let capacity = vec.capacity();
std::mem::forget(vec);
RawVec {
ptr,
capacity,
marker: PhantomData,
}
}
/// Convert this value into a vector, without consuming ownership.
///
/// # Safety
/// Will lead to double-free if both theis `RawVec` and the returned vec are dropped.
#[inline]
unsafe fn as_vec_unchecked(&self) -> ManuallyDrop<Vec<ManuallyDrop<T>>> {
// SAFETY: Parts are valid
unsafe {
if Self::NEED_DANGLING_CHECK && self.capacity == 0 {
ManuallyDrop::new(Vec::new())
} else {
ManuallyDrop::new(Vec::from_raw_parts(
self.ptr.as_ptr(),
0, // no length
self.capacity,
))
}
}
}
}
#[cfg(feature = "nightly")]
impl<#[may_dangle] T> Drop for RawVec<T> {
fn drop(&mut self) {
// SAFETY: No double drop due to being called in drop
drop(unsafe { self.as_vec_unchecked() });
}
}
#[cfg(not(feature = "nightly"))]
impl<T> Drop for RawVec<T> {
fn drop(&mut self) {
// SAFETY: No double drop due to being called in drop
drop(unsafe { self.as_vec_unchecked() });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment