#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc, sync::Arc};
#[cfg(feature = "alloc")]
use core::alloc::Allocator;
use core::{
fmt,
marker::{CoercePointee, PhantomData},
mem::{self, ManuallyDrop, MaybeUninit},
ops::Deref,
pin::Pin,
ptr::{self, NonNull},
};
use self::construct::PlaceConstruct;
use crate::{
init::{IntoInit, IntoInitPin},
owned::Own,
pin::{DropSlot, POwn},
sealed,
uninit::Uninit,
};
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a place for type `{T}`",
label = "`{Self}` is not a place for type `{T}`"
)]
pub unsafe trait Place<T: ?Sized>: Sized {
type Init;
fn as_mut_ptr(&mut self) -> *mut T;
unsafe fn assume_init(self) -> Self::Init;
fn from_init(init: Self::Init) -> Self;
#[inline]
fn write<'b, M, I>(&'b mut self, init: I) -> Own<'b, T>
where
I: IntoInit<T, M, Error: fmt::Debug>,
{
Uninit::from_mut(self).write(init)
}
#[inline]
fn write_pin<'a, 'b, M, I>(&'a mut self, init: I, slot: DropSlot<'a, 'b, T>) -> POwn<'b, T>
where
I: IntoInitPin<T, M, Error: fmt::Debug>,
{
Uninit::from_mut(self).write_pin(init, slot)
}
#[inline]
fn init<M, I, E>(self, init: I) -> Self::Init
where
I: IntoInit<T, M, Error = E>,
E: fmt::Debug,
{
self.try_init(init).map_err(|(e, _)| e).unwrap()
}
#[inline]
fn try_init<M, I, E>(mut self, init: I) -> Result<Self::Init, (E, Self)>
where
I: IntoInit<T, M, Error = E>,
{
'ok: {
let err = match Uninit::from_mut(&mut self).try_write(init) {
Ok(own) => break 'ok mem::forget(own),
Err(err) => (mem::forget(err.place), err.error).1,
};
return Err((err, self));
}
Ok(unsafe { self.assume_init() })
}
#[inline]
fn init_pin<M, I, E>(self, init: I) -> Pin<Self::Init>
where
I: IntoInitPin<T, M, Error = E>,
Self::Init: Deref,
E: fmt::Debug,
{
self.try_init_pin(init).map_err(|(e, _)| e).unwrap()
}
#[inline]
fn try_init_pin<M, I, E>(mut self, init: I) -> Result<Pin<Self::Init>, (E, Self)>
where
I: IntoInitPin<T, M, Error = E>,
Self::Init: Deref,
{
'ok: {
let mut slot = ManuallyDrop::new(crate::pin::DroppingSlot::new());
let sr = unsafe { crate::pin::DropSlot::new_unchecked(&mut slot) };
let err = match Uninit::from_mut(&mut self).try_write_pin(init, sr) {
Ok(own) => break 'ok mem::forget(own),
Err(err) => (mem::forget((err.place, err.slot)), err.error).1,
};
return Err((err, self));
}
Ok(unsafe { Pin::new_unchecked(self.assume_init()) })
}
}
pub mod construct;
unsafe impl<T> Place<T> for MaybeUninit<T> {
type Init = T;
#[inline]
fn as_mut_ptr(&mut self) -> *mut T {
self.as_mut_ptr()
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe { self.assume_init() }
}
#[inline]
fn from_init(init: Self::Init) -> Self {
Self::new(init)
}
}
unsafe impl<const N: usize> Place<str> for MaybeUninit<[u8; N]> {
type Init = [u8; N];
#[inline]
fn as_mut_ptr(&mut self) -> *mut str {
let ptr = self.as_mut_ptr().cast::<u8>();
ptr::from_raw_parts_mut(ptr, N)
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe { self.assume_init() }
}
#[inline]
fn from_init(init: Self::Init) -> Self {
MaybeUninit::new(init)
}
}
unsafe impl<T, U: ?Sized, const N: usize> Place<U> for [MaybeUninit<T>; N]
where
MaybeUninit<[T; N]>: Place<U>,
{
type Init = [T; N];
#[inline]
fn as_mut_ptr(&mut self) -> *mut U {
let r =
unsafe { mem::transmute::<&mut [MaybeUninit<T>; N], &mut MaybeUninit<[T; N]>>(self) };
Place::as_mut_ptr(r)
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe { self.map(|p| MaybeUninit::assume_init(p)) }
}
#[inline]
fn from_init(init: Self::Init) -> Self {
init.map(MaybeUninit::new)
}
}
#[cfg(feature = "alloc")]
macro_rules! std_alloc_places {
($($ty:ident: [$($mut:ident)?; $($try_slice:ident)?]),* $(,)?) => {
$(std_alloc_places!(@IMP $ty: [$($mut)?; $($try_slice)?]);)*
};
(@IMP $ty:ident: [$($mut:ident)?; $($try_slice:ident)?]) => {
unsafe impl<T, A: Allocator> Place<T> for $ty<MaybeUninit<T>, A> {
type Init = $ty<T, A>;
#[inline]
fn as_mut_ptr(&mut self) -> *mut T {
std_alloc_places!(@get_mut self, $($mut)? $ty).as_mut_ptr()
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe { self.assume_init() }
}
#[inline]
fn from_init(init: Self::Init) -> Self {
let (raw, alloc) = $ty::into_raw_with_allocator(init);
unsafe { $ty::from_raw_in(raw.cast::<MaybeUninit<T>>(), alloc) }
}
}
impl<T> PlaceConstruct<()> for $ty<T> {
type Uninit = $ty<MaybeUninit<T>>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(_: ()) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit()
}
}
impl<T, A: Allocator> PlaceConstruct<A> for $ty<T, A> {
type Uninit = $ty<MaybeUninit<T>, A>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(alloc: A) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit_in(alloc)
}
}
unsafe impl<T, A: Allocator> Place<[T]> for $ty<[MaybeUninit<T>], A> {
type Init = $ty<[T], A>;
#[inline]
fn as_mut_ptr(&mut self) -> *mut [T] {
let len = self.len();
let ptr = std_alloc_places!(@get_mut self, $($mut)? $ty).as_mut_ptr();
ptr::from_raw_parts_mut(ptr.cast::<T>(), len)
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe { self.assume_init() }
}
#[inline]
fn from_init(init: Self::Init) -> Self {
let len = init.len();
let (raw, alloc) = $ty::into_raw_with_allocator(init);
let ptr = std_alloc_places!(@from_raw_parts $($mut)? (
raw.cast::<MaybeUninit<T>>(),
len,
));
unsafe { $ty::from_raw_in(ptr, alloc) }
}
}
unsafe impl<A: Allocator> Place<str> for $ty<[MaybeUninit<u8>], A> {
type Init = $ty<str, A>;
#[inline]
fn as_mut_ptr(&mut self) -> *mut str {
let len = self.len();
let ptr = std_alloc_places!(@get_mut self, $($mut)? $ty).as_mut_ptr();
ptr::from_raw_parts_mut(ptr.cast::<u8>(), len)
}
#[inline]
unsafe fn assume_init(self) -> Self::Init {
unsafe {
let (raw, alloc) = $ty::into_raw_with_allocator(self.assume_init());
let ptr = std_alloc_places!(@from_raw_parts $($mut)? (
raw.cast::<u8>(),
raw.len(),
));
$ty::from_raw_in(ptr, alloc)
}
}
#[inline]
fn from_init(init: Self::Init) -> Self {
let len = init.len();
let (raw, alloc) = $ty::into_raw_with_allocator(init);
let ptr = std_alloc_places!(@from_raw_parts $($mut)? (
raw.cast::<MaybeUninit<u8>>(),
len,
));
unsafe { $ty::from_raw_in(ptr, alloc) }
}
}
std_alloc_places!(@TRY_SLICE $($try_slice)? $ty);
};
(@get_mut $this:ident, $ty:ident) => {
$ty::get_mut($this).unwrap()
};
(@get_mut $this:ident, mut $ty:ident) => {
**$this
};
(@from_raw_parts ($($t:tt)*)) => {
ptr::from_raw_parts($($t)*)
};
(@from_raw_parts mut ($($t:tt)*)) => {
ptr::from_raw_parts_mut($($t)*)
};
(@TRY_SLICE try_slice $ty:ident) => {
impl<T> PlaceConstruct<usize> for $ty<[T]> {
type Uninit = $ty<[MaybeUninit<T>]>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(len: usize) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit_slice(len)
}
}
impl<T, A: Allocator> PlaceConstruct<(usize, A)> for $ty<[T], A> {
type Uninit = $ty<[MaybeUninit<T>], A>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in((len, alloc): (usize, A)) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit_slice_in(len, alloc)
}
}
impl PlaceConstruct<usize> for $ty<str> {
type Uninit = $ty<[MaybeUninit<u8>]>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(len: usize) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit_slice(len)
}
}
impl<A: Allocator> PlaceConstruct<(usize, A)> for $ty<str, A> {
type Uninit = $ty<[MaybeUninit<u8>], A>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in((len, alloc): (usize, A)) -> Result<Self::Uninit, Self::Error> {
$ty::try_new_uninit_slice_in(len, alloc)
}
}
};
(@TRY_SLICE $ty:ident) => {
impl<T> PlaceConstruct<usize> for $ty<[T]> {
type Uninit = $ty<[MaybeUninit<T>]>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(len: usize) -> Result<Self::Uninit, Self::Error> {
Ok($ty::new_uninit_slice(len))
}
}
impl<T, A: Allocator> PlaceConstruct<(usize, A)> for $ty<[T], A> {
type Uninit = $ty<[MaybeUninit<T>], A>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in((len, alloc): (usize, A)) -> Result<Self::Uninit, Self::Error> {
Ok($ty::new_uninit_slice_in(len, alloc))
}
}
impl PlaceConstruct<usize> for $ty<str> {
type Uninit = $ty<[MaybeUninit<u8>]>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in(len: usize) -> Result<Self::Uninit, Self::Error> {
Ok($ty::new_uninit_slice(len))
}
}
impl<A: Allocator> PlaceConstruct<(usize, A)> for $ty<str, A> {
type Uninit = $ty<[MaybeUninit<u8>], A>;
type Error = alloc::alloc::AllocError;
#[inline]
fn try_new_uninit_in((len, alloc): (usize, A)) -> Result<Self::Uninit, Self::Error> {
Ok($ty::new_uninit_slice_in(len, alloc))
}
}
}
}
#[cfg(feature = "alloc")]
std_alloc_places! {
Box: [mut; ],
Rc: [;],
Arc: [;],
}
pub enum Owned {}
pub enum Uninitialized {}
pub trait PlaceState: sealed::Sealed {
#[doc(hidden)]
#[allow(private_interfaces)]
unsafe fn drop<T: ?Sized>(inner: *mut T);
}
impl sealed::Sealed for Owned {}
impl PlaceState for Owned {
#[allow(private_interfaces)]
#[inline]
unsafe fn drop<T: ?Sized>(inner: *mut T) {
unsafe { inner.drop_in_place() };
}
}
impl sealed::Sealed for Uninitialized {}
impl PlaceState for Uninitialized {
#[allow(private_interfaces)]
#[inline]
unsafe fn drop<T: ?Sized>(_inner: *mut T) {
}
}
#[derive(CoercePointee)]
#[repr(transparent)]
pub struct PlaceRef<'a, #[pointee] T: ?Sized, State: PlaceState> {
pub(crate) inner: NonNull<T>,
state: PhantomData<(State, &'a mut MaybeUninit<PhantomData<T>>)>,
owns_value: PhantomData<T>,
}
unsafe impl<'a, T: ?Sized + Send, S: PlaceState> Send for PlaceRef<'a, T, S> {}
unsafe impl<'a, T: ?Sized + Sync, S: PlaceState> Sync for PlaceRef<'a, T, S> {}
impl<'a, T: ?Sized, S: PlaceState> Unpin for PlaceRef<'a, T, S> {}
impl<'a, T: ?Sized, S: PlaceState> PlaceRef<'a, T, S> {
#[inline]
pub(crate) const unsafe fn from_inner(inner: NonNull<T>) -> Self {
PlaceRef {
inner,
state: PhantomData,
owns_value: PhantomData,
}
}
}
unsafe impl<'a, #[may_dangle] T: ?Sized, S: PlaceState> Drop for PlaceRef<'a, T, S> {
#[inline]
fn drop(&mut self) {
unsafe { S::drop::<T>(self.inner.as_ptr()) };
}
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::{init, place::construct::PlaceStr};
#[test]
fn places() {
let b = Box::new_str(5, init::str("hello"));
assert_eq!(&*b, "hello");
let (e, _) = Box::new_uninit_slice(7)
.try_init(init::str("world"))
.unwrap_err();
assert_eq!(e, init::SliceError);
}
}