Trait uninit::extension_traits::VecCapacity
source · pub trait VecCapacity: Sealed {
type Item;
// Required methods
fn split_at_extra_cap(
&mut self
) -> (&mut [Self::Item], &mut [MaybeUninit<Self::Item>]);
fn spare_cap(&mut self) -> &mut [MaybeUninit<Self::Item>];
fn reserve_uninit(
&mut self,
additional: usize
) -> &mut [MaybeUninit<Self::Item>];
fn get_backing_buffer(&mut self) -> Out<'_, [Self::Item]>
where Self::Item: Copy;
fn into_backing_buffer(self) -> Box<[MaybeUninit<Self::Item>]>
where Self::Item: Copy;
fn get_backing_buffer_with_leaking_writes(
&mut self
) -> Out<'_, [Self::Item]>;
fn into_backing_buffer_forget_elems(self) -> Box<[MaybeUninit<Self::Item>]>;
}
alloc
or std
only.Expand description
Extension trait for Vec
, allowing a non-unsafe
API to interact
with the backing buffer / allocation.
Required Associated Types§
Required Methods§
sourcefn split_at_extra_cap(
&mut self
) -> (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])
fn split_at_extra_cap( &mut self ) -> (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])
See ↓ below ↓.
sourcefn spare_cap(&mut self) -> &mut [MaybeUninit<Self::Item>]
fn spare_cap(&mut self) -> &mut [MaybeUninit<Self::Item>]
See ↓ below ↓.
sourcefn reserve_uninit(
&mut self,
additional: usize
) -> &mut [MaybeUninit<Self::Item>]
fn reserve_uninit( &mut self, additional: usize ) -> &mut [MaybeUninit<Self::Item>]
See ↓ below ↓.
sourcefn get_backing_buffer(&mut self) -> Out<'_, [Self::Item]>where
Self::Item: Copy,
fn get_backing_buffer(&mut self) -> Out<'_, [Self::Item]>where Self::Item: Copy,
See ↓ below ↓.
sourcefn into_backing_buffer(self) -> Box<[MaybeUninit<Self::Item>]>where
Self::Item: Copy,
fn into_backing_buffer(self) -> Box<[MaybeUninit<Self::Item>]>where Self::Item: Copy,
See ↓ below ↓.
sourcefn get_backing_buffer_with_leaking_writes(&mut self) -> Out<'_, [Self::Item]>
fn get_backing_buffer_with_leaking_writes(&mut self) -> Out<'_, [Self::Item]>
See ↓ below ↓.
sourcefn into_backing_buffer_forget_elems(self) -> Box<[MaybeUninit<Self::Item>]>
fn into_backing_buffer_forget_elems(self) -> Box<[MaybeUninit<Self::Item>]>
See ↓ below ↓.
Object Safety§
Implementations on Foreign Types§
source§impl<T> VecCapacity for Vec<T>
impl<T> VecCapacity for Vec<T>
source§fn spare_cap(self: &mut Vec<T>) -> &mut [MaybeUninit<T>]
fn spare_cap(self: &mut Vec<T>) -> &mut [MaybeUninit<T>]
Same as .split_at_extra_cap().1
, but for not invalidating
—w.r.t. aliasing / Stacked Borrows— pointers to the initialized area
of this Vec
:
use ::uninit::prelude::*;
let mut v = Vec::with_capacity(2); // len = 0 && 2 uninit
v.push(0); // len = 1 && 1 uninit
let at_fst = v.as_mut_ptr();
v.spare_cap()[0] = MaybeUninit::new(27); // len = 1 && 1 init
unsafe {
v.set_len(2); // len = 2
*at_fst += 42; // OK, neither `spare_cap()` nor the `len` interactions invalidated this.
}
assert_eq!(v.iter().sum::<i32>(), 42 + 27);
source§fn split_at_extra_cap(self: &mut Vec<T>) -> (&mut [T], &mut [MaybeUninit<T>])
fn split_at_extra_cap(self: &mut Vec<T>) -> (&mut [T], &mut [MaybeUninit<T>])
Splits the Vec<T>
’s
backing buffer into two slices of
initialized and uninitialized elements.
Imagine this as doing
self.get_backing_buffer().split_at_out(self.len())
while upgrading the first half to &mut [T]
.
Guarantees (that unsafe
code may rely on)
Given a vector v
, and let (xs, extra) = v.split_at_extra_cap()
,
then:
-
xs
isv.as_slice()
, so:-
xs.len() == v.len()
, -
xs.as_ptr() == v.as_ptr()
,
-
-
extra.len() == (v.capacity() - v.len())
;-
if
.split_at_extra_cap()
is called right after a call to.reserve(n)
, thenv.capacity() ≥ v.len() + n
, and thusextra.len() ≥ n
.For the
extra.len() == n
equality to hold, one must subsliceextra
:extra = extra.get_out(.. n).unwrap();
.
And given the aformentioned guarantees, one can even:
extra = extra.get_unchecked_out(.. n);
This last idiom is covered by
.reserve_uninit(n)
.
-
-
extra.as_ptr() == v.as_ptr().add(v.len())
. -
Thus, only after initializing the first
k
elements ofextra
, is it sound tov.set_len(v.len() + k);
.
Example
Making a palindrome Vec
:
use ::uninit::prelude::*;
fn make_palindrome<T : Copy> (v: &'_ mut Vec<T>)
{
let len = v.len();
v.reserve(len);
let (xs, extra) = v.split_at_extra_cap();
for (&x, at_dst) in xs.iter().rev().zip(extra.as_out()) {
at_dst.write(x);
}
unsafe {
// Safety: the first `len` elements of `extra` have been initialized.
v.set_len(2 * len);
}
}
let mut v = vec![1, 2, 3];
make_palindrome(&mut v);
assert_eq!(v, [1, 2, 3, 3, 2, 1]);
source§fn reserve_uninit(self: &mut Vec<T>, additional: usize) -> &mut [MaybeUninit<T>]
fn reserve_uninit(self: &mut Vec<T>, additional: usize) -> &mut [MaybeUninit<T>]
Reserves extra (uninitialized) memory for it, returning a mutable handle to those extra (uninitialized) elements.
Example
use ::uninit::prelude::*;
let mut vec = b"Hello, ".to_vec();
const WORLD: &[u8] = b"World!";
let mut extra: Out<'_, [u8]> = vec.reserve_uninit(WORLD.len()).as_out();
extra.r().copy_from_slice(WORLD);
// `.reserve_uninit()` guarantees the following properties:
assert_eq!(extra.len(), WORLD.len());
let extra_start: *mut u8 = extra.r().as_mut_ptr().cast();
let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len());
assert_eq!(extra_start, uninit_start);
unsafe {
// # Safety
//
// - `.copy_from_slice()` contract guarantees initialization
// of `extra`, which, in turn, from `reserve_uninit`'s contract,
// leads to the `vec` extra capacity having been initialized.
vec.set_len(vec.len() + WORLD.len());
}
assert_eq!(
vec,
b"Hello, World!",
);
source§fn get_backing_buffer(self: &mut Vec<T>) -> Out<'_, [T]>where
T: Copy,
fn get_backing_buffer(self: &mut Vec<T>) -> Out<'_, [T]>where T: Copy,
Gets an &out [T]
slice (of self.capacity()
elements)
to the backing buffer.
source§fn get_backing_buffer_with_leaking_writes(self: &mut Vec<T>) -> Out<'_, [T]>
fn get_backing_buffer_with_leaking_writes(self: &mut Vec<T>) -> Out<'_, [T]>
Same as .get_backing_buffer()
but without the Copy
bound.
This means that extra care should be taken if
mem::needs_drop::<Self::Item>()
. Indeed, if the OutSlice
is used to
overwrite initialized elements, then such elements will be destroyed
without their .drop()
glue / destructors ever being run.
⚠️ Misusage of this function can thus lead to memory leaks ⚠️
Counter-example
use ::uninit::prelude::*;
use ::std::rc::Rc;
let rc = Rc::new(());
assert_eq!(Rc::strong_count(&rc), 1);
let mut v = vec![ Some(Rc::clone(&rc)) ];
assert_eq!(Rc::strong_count(&rc), 2);
// This overwrites the `rc` clone without running any destructor
// whatsoever, hence leaking it.
v .get_backing_buffer_with_leaking_writes()
.get_out(0)
.unwrap()
.write(None) // the `rc` clone is not freed
;
assert_eq!(Rc::strong_count(&rc), 2);
assert!(Rc::try_unwrap(rc).is_err());
Example
use ::uninit::prelude::*;
use ::std::cell::Cell;
let mut v = vec![Cell::new(0)];
v .get_backing_buffer_with_leaking_writes() // No drop glue, so this is fine
.get_out(0)
.unwrap()
.write(Cell::new(42))
;
assert_eq!(v[0].get(), 42);
source§fn into_backing_buffer(self: Vec<T>) -> Box<[MaybeUninit<T>]>where
T: Copy,
fn into_backing_buffer(self: Vec<T>) -> Box<[MaybeUninit<T>]>where T: Copy,
Extracts an owned handle to the backing buffer.
source§fn into_backing_buffer_forget_elems(self: Vec<T>) -> Box<[MaybeUninit<T>]>
fn into_backing_buffer_forget_elems(self: Vec<T>) -> Box<[MaybeUninit<T>]>
Same as .into_backing_buffer()
but without the Copy
bound.
This means that extra care should be taken if
mem::needs_drop::<Self::Item>()
. Indeed, the returned boxed slice
will not run the destructor of its initialized elements (since it no
longer knows which are).
⚠️ Misusage of this function can thus lead to memory leaks ⚠️
Counter-example
use ::uninit::prelude::*;
use ::std::rc::Rc;
let rc = Rc::new(());
assert_eq!(Rc::strong_count(&rc), 1);
let mut v = vec![ Some(Rc::clone(&rc)) ];
assert_eq!(Rc::strong_count(&rc), 2);
// This leaks the `rc` clone (but not the heap-allocated array containing it)
let _ = v.into_backing_buffer_forget_elems();
assert_eq!(Rc::strong_count(&rc), 2);
assert!(Rc::try_unwrap(rc).is_err());
Example
use ::uninit::prelude::*;
let mut v = vec![String::from("Hello!")];
// Good practice: before calling `.into_backing_buffer_forget_elems()`
// one ought to `.clear()` the `Vec`:
v.clear(); // drops `"Hello!"`
let mut strings_buffer: Box<[MaybeUninit<String>]> =
v.into_backing_buffer_forget_elems()
;
strings_buffer[0] = MaybeUninit::new(String::from("Greetings!"));
let strings_buffer: Box<[String]> = unsafe {
Box::assume_init(strings_buffer)
};
assert_eq!(&*strings_buffer[0], "Greetings!");
// This does free the contained "Greetings!" `String`.
drop(strings_buffer);