// SPDX-License-Identifier: Apache-2.0 OR MIT #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::{boxed::Box, sync::Arc}; #[cfg(feature = "alloc")] use core::alloc::AllocError; use core::{mem::MaybeUninit, pin::Pin}; #[cfg(feature = "std")] use std::sync::Arc; #[cfg(not(feature = "alloc"))] type AllocError = core::convert::Infallible; use crate::{ init_from_closure, pin_init_from_closure, InPlaceWrite, Init, PinInit, ZeroableOption, }; pub extern crate alloc; // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee: // ). unsafe impl ZeroableOption for Box {} /// Smart pointer that can initialize memory in-place. pub trait InPlaceInit: Sized { /// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. fn try_pin_init(init: impl PinInit) -> Result, E> where E: From; /// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. fn pin_init(init: impl PinInit) -> Result, AllocError> { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { pin_init_from_closure(|slot| match init.__pinned_init(slot) { Ok(()) => Ok(()), Err(i) => match i {}, }) }; Self::try_pin_init(init) } /// Use the given initializer to in-place initialize a `T`. fn try_init(init: impl Init) -> Result where E: From; /// Use the given initializer to in-place initialize a `T`. fn init(init: impl Init) -> Result { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { init_from_closure(|slot| match init.__init(slot) { Ok(()) => Ok(()), Err(i) => match i {}, }) }; Self::try_init(init) } } #[cfg(feature = "alloc")] macro_rules! try_new_uninit { ($type:ident) => { $type::try_new_uninit()? }; } #[cfg(all(feature = "std", not(feature = "alloc")))] macro_rules! try_new_uninit { ($type:ident) => { $type::new_uninit() }; } impl InPlaceInit for Box { #[inline] fn try_pin_init(init: impl PinInit) -> Result, E> where E: From, { try_new_uninit!(Box).write_pin_init(init) } #[inline] fn try_init(init: impl Init) -> Result where E: From, { try_new_uninit!(Box).write_init(init) } } impl InPlaceInit for Arc { #[inline] fn try_pin_init(init: impl PinInit) -> Result, E> where E: From, { let mut this = try_new_uninit!(Arc); let Some(slot) = Arc::get_mut(&mut this) else { // SAFETY: the Arc has just been created and has no external references unsafe { core::hint::unreachable_unchecked() } }; let slot = slot.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. unsafe { init.__pinned_init(slot)? }; // SAFETY: All fields have been initialized and this is the only `Arc` to that data. Ok(unsafe { Pin::new_unchecked(this.assume_init()) }) } #[inline] fn try_init(init: impl Init) -> Result where E: From, { let mut this = try_new_uninit!(Arc); let Some(slot) = Arc::get_mut(&mut this) else { // SAFETY: the Arc has just been created and has no external references unsafe { core::hint::unreachable_unchecked() } }; let slot = slot.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized. Ok(unsafe { this.assume_init() }) } } impl InPlaceWrite for Box> { type Initialized = Box; fn write_init(mut self, init: impl Init) -> Result { let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized. Ok(unsafe { self.assume_init() }) } fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. unsafe { init.__pinned_init(slot)? }; // SAFETY: All fields have been initialized. Ok(unsafe { self.assume_init() }.into()) } }