Responding to your “unsafe code in the wild” thread here so as not to contaminate it:
This looks like a case of missing abstractions and I’m sure it’s not the only one. In this case, a write-only UninitializedVec<T> abstraction would probably help. Something like:
#![feature(untagged_unions)]
use std::ops::{Index, IndexMut};
use std::ptr::drop_in_place;
pub struct UninitializedVec<T: Sized> {
data: *mut T,
len: usize,
capacity: usize,
}
impl<T: Sized> UninitializedVec<T> {
// All the normal methods except that they operate over `Uninit<T>` instead of `T`...
// And:
pub unsafe fn into_vec(self) -> Vec<T> {
Vec::from_raw_parts(self.data, self.len, self.capacity)
}
}
impl<T: Sized> Index<usize> for UninitializedVec<T> {
type Output = Uninit<T>;
fn index(&self, idx: usize) -> &Uninit<T> {
unsafe {
assert!(idx < self.len, "out of bounds");
&*(self.data.offset(idx as isize) as *const Uninit<T>)
}
}
}
impl<T: Sized> IndexMut<usize> for UninitializedVec<T> {
fn index_mut(&mut self, idx: usize) -> &mut Uninit<T> {
unsafe {
assert!(idx < self.len, "out of bounds");
&mut *(self.data.offset(idx as isize) as *mut Uninit<T>)
}
}
}
// I assume the alignment/size will match T...
#[allow(unions_with_drop_fields)]
pub union Uninit<T> {
inited: T,
}
impl<T> Uninit<T> {
/// Set to `value`. This will forget the existing value, if any.
pub fn set(&mut self, value: T) {
unsafe {
self.inited = value;
}
}
/// Set to `value` dropping the existing one.
pub unsafe fn replace(&mut self, value: T) {
drop_in_place(&mut self.inited);
self.inited = value;
}
pub unsafe fn get_unchecked(&self) -> &T {
&self.inited
}
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
&mut self.inited
}
}