#![no_std]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![warn(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod unique;
pub use pin_init_internal::pin_init;
#[cfg(feature = "alloc")]
pub use unique::{UniqueArc, UniqueRc};
use core::marker::PhantomData;
use core::mem;
use core::mem::MaybeUninit;
use core::pin::Pin;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc, sync::Arc};
#[cfg(feature = "alloc")]
use core::mem::ManuallyDrop;
pub struct PinInit<'a, T> {
ptr: *mut MaybeUninit<T>,
_marker: PhantomData<&'a mut ()>,
}
impl<'a, T> PinInit<'a, T> {
#[inline]
pub unsafe fn new(ptr: &'a mut MaybeUninit<T>) -> Self {
PinInit {
ptr,
_marker: PhantomData,
}
}
#[inline]
pub fn get_mut(&mut self) -> &mut MaybeUninit<T> {
unsafe { &mut *self.ptr }
}
#[inline]
pub unsafe fn init_ok(self) -> PinInitOk<'a, T> {
PinInitOk {
ptr: self.ptr as *mut T,
marker: PhantomData,
}
}
#[inline]
pub fn init_err<E>(self, err: E) -> PinInitErr<'a, E> {
PinInitErr {
inner: err,
marker: PhantomData,
}
}
#[inline]
pub fn init<E, F>(self, value: F) -> PinInitResult<'a, T, E>
where
F: FnOnce(Self) -> PinInitResult<'a, T, E>,
{
value(self)
}
#[inline]
pub fn init_with_value(mut self, value: T) -> PinInitOk<'a, T> {
unsafe { self.get_mut().as_mut_ptr().write(value) };
unsafe { self.init_ok() }
}
}
pub struct PinInitOk<'a, T> {
ptr: *mut T,
marker: PhantomData<&'a mut ()>,
}
impl<'a, T> PinInitOk<'a, T> {
#[inline]
pub fn as_ref(&self) -> Pin<&T> {
unsafe { Pin::new_unchecked(&*self.ptr) }
}
#[inline]
pub fn as_mut(&mut self) -> Pin<&mut T> {
unsafe { Pin::new_unchecked(&mut *self.ptr) }
}
#[inline]
pub fn into_inner(self) -> Pin<&'a mut T> {
unsafe { Pin::new_unchecked(&mut *self.ptr) }
}
}
pub struct PinInitErr<'a, E> {
inner: E,
marker: PhantomData<&'a mut ()>,
}
impl<'a, E> PinInitErr<'a, E> {
#[inline]
pub fn as_ref(&self) -> &E {
&self.inner
}
#[inline]
pub fn as_mut(&mut self) -> &mut E {
&mut self.inner
}
#[inline]
pub fn into_inner(self) -> E {
self.inner
}
#[inline]
pub fn map<T, F>(self, f: F) -> PinInitErr<'a, T>
where
F: FnOnce(E) -> T,
{
PinInitErr {
inner: f(self.inner),
marker: PhantomData,
}
}
}
pub type PinInitResult<'a, T, E> = Result<PinInitOk<'a, T>, PinInitErr<'a, E>>;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn init_box<T, E, F>(x: Pin<Box<MaybeUninit<T>>>, f: F) -> Result<Pin<Box<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
let mut ptr = ManuallyDrop::new(unsafe { Pin::into_inner_unchecked(x) });
match f(unsafe { PinInit::new(&mut ptr) }) {
Ok(_) => {
Ok(unsafe { mem::transmute(ptr) })
}
Err(err) => {
let err = err.into_inner();
drop(ManuallyDrop::into_inner(ptr));
Err(err)
}
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn init_unique_rc<T, E, F>(
x: Pin<UniqueRc<MaybeUninit<T>>>,
f: F,
) -> Result<Pin<UniqueRc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
let mut ptr = ManuallyDrop::new(unsafe { Pin::into_inner_unchecked(x) });
match f(unsafe { PinInit::new(&mut ptr) }) {
Ok(_) => Ok(unsafe { mem::transmute(ptr) }),
Err(err) => {
let err = err.into_inner();
drop(ManuallyDrop::into_inner(ptr));
Err(err)
}
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn init_unique_arc<T, E, F>(
x: Pin<UniqueArc<MaybeUninit<T>>>,
f: F,
) -> Result<Pin<UniqueArc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
let mut ptr = ManuallyDrop::new(unsafe { Pin::into_inner_unchecked(x) });
match f(unsafe { PinInit::new(&mut ptr) }) {
Ok(_) => Ok(unsafe { mem::transmute(ptr) }),
Err(err) => {
let err = err.into_inner();
drop(ManuallyDrop::into_inner(ptr));
Err(err)
}
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn new_box<T, E, F>(f: F) -> Result<Pin<Box<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
init_box(Box::pin(MaybeUninit::uninit()), f)
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn new_unique_rc<T, E, F>(f: F) -> Result<Pin<UniqueRc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
init_unique_rc(UniqueRc::new_uninit().into(), f)
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn new_unique_arc<T, E, F>(f: F) -> Result<Pin<UniqueArc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
init_unique_arc(UniqueArc::new_uninit().into(), f)
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn new_rc<T, E, F>(f: F) -> Result<Pin<Rc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
Ok(UniqueRc::shareable_pin(new_unique_rc(f)?))
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn new_arc<T, E, F>(f: F) -> Result<Pin<Arc<T>>, E>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
Ok(UniqueArc::shareable_pin(new_unique_arc(f)?))
}
pub trait PinInitable<'this>: Sized {
#[doc(hidden)]
type __PinInitBuilder;
#[doc(hidden)]
fn __pin_init_builder(init: PinInit<'this, Self>) -> Self::__PinInitBuilder;
}
#[doc(hidden)]
pub mod __private {
use super::*;
pub use pin_init_internal::PinInit;
pub struct StackWrapper<T>(MaybeUninit<T>, bool);
impl<T> StackWrapper<T> {
#[inline]
pub fn new() -> Self {
StackWrapper(MaybeUninit::uninit(), false)
}
#[inline]
pub fn init<F, E>(self: Pin<&mut Self>, f: F) -> Result<Pin<&mut T>, E>
where
F: for<'b> FnOnce(PinInit<'b, T>) -> PinInitResult<'b, T, E>,
{
struct PanicGuard;
impl Drop for PanicGuard {
#[inline]
fn drop(&mut self) {
panic!("panicked while pin-initing variable on stack");
}
}
assert!(!self.1);
let this = unsafe { self.get_unchecked_mut() };
let g = PanicGuard;
let res = f(unsafe { PinInit::new(&mut this.0) });
mem::forget(g);
match res {
Ok(ok) => {
this.1 = true;
Ok(ok.into_inner())
}
Err(err) => Err(err.into_inner()),
}
}
}
impl<T> Drop for StackWrapper<T> {
#[inline]
fn drop(&mut self) {
if self.1 {
unsafe {
self.0.as_mut_ptr().drop_in_place();
}
}
}
}
pub struct ValueBuilder<'this, T>(PinInitOk<'this, T>);
impl<'this, T> ValueBuilder<'this, T> {
#[inline]
pub fn __init_ok(self) -> PinInitOk<'this, T> {
self.0
}
}
impl<'this> PinInitable<'this> for core::marker::PhantomPinned {
#[doc(hidden)]
type __PinInitBuilder = ValueBuilder<'this, Self>;
#[doc(hidden)]
#[inline]
fn __pin_init_builder(init: PinInit<'this, Self>) -> Self::__PinInitBuilder {
ValueBuilder(init.init_with_value(Self))
}
}
pub struct TransparentBuilder<'this, T, W>(PinInit<'this, W>, PhantomData<PinInit<'this, T>>);
impl<'this, T> PinInitable<'this> for core::cell::UnsafeCell<T> {
#[doc(hidden)]
type __PinInitBuilder = TransparentBuilder<'this, T, core::cell::UnsafeCell<T>>;
#[doc(hidden)]
#[inline]
fn __pin_init_builder(init: PinInit<'this, Self>) -> Self::__PinInitBuilder {
TransparentBuilder(init, PhantomData)
}
}
impl<'this, T> PinInitable<'this> for core::cell::Cell<T> {
#[doc(hidden)]
type __PinInitBuilder = TransparentBuilder<'this, T, core::cell::Cell<T>>;
#[doc(hidden)]
#[inline]
fn __pin_init_builder(init: PinInit<'this, Self>) -> Self::__PinInitBuilder {
TransparentBuilder(init, PhantomData)
}
}
impl<'this, T, W> TransparentBuilder<'this, T, W> {
#[inline]
pub fn __next<E, F>(mut self, f: F) -> Result<ValueBuilder<'this, W>, PinInitErr<'this, E>>
where
F: for<'a> FnOnce(PinInit<'a, T>) -> PinInitResult<'a, T, E>,
{
let ptr = self.0.get_mut().as_mut_ptr() as *mut MaybeUninit<T>;
match f(unsafe { PinInit::new(&mut *ptr) }) {
Ok(_) => Ok(ValueBuilder(unsafe { self.0.init_ok() })),
Err(err) => Err(self.0.init_err(err.into_inner())),
}
}
}
}
#[macro_export]
macro_rules! init_stack {
($var:ident = $init:expr) => {
let mut storage = $crate::__private::StackWrapper::new();
let $var = unsafe { Pin::new_unchecked(&mut storage) }.init($init);
};
}
#[macro_export]
macro_rules! init_pin {
(try $err:ty => $ty:ident { $($tt:tt)* }) => {{
|this| -> $crate::PinInitResult<'_, _, $err> {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
$crate::__init_pin_internal!(@named builder => $($tt)*,);
Ok(builder.__init_ok())
}
}};
(try $err:ty => $ty:ident ( $($tt:tt)* )) => {{
|this| -> $crate::PinInitResult<'_, _, $err> {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
$crate::__init_pin_internal!(@unnamed builder => $($tt)*,);
Ok(builder.__init_ok())
}
}};
(try $err:ty => $ty:ident) => {{
|this| -> $crate::PinInitResult<'_, _, $err> {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
Ok(builder.__init_ok())
}
}};
($ty:ident { $($tt:tt)* }) => {{
|this| {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
$crate::__init_pin_internal!(@named builder => $($tt)*,);
Ok(builder.__init_ok())
}
}};
($ty:ident ( $($tt:tt)* )) => {{
|this| {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
$crate::__init_pin_internal!(@unnamed builder => $($tt)*,);
Ok(builder.__init_ok())
}
}};
($ty:ident) => {{
|this| {
use $crate::PinInitable;
let builder = $ty::__pin_init_builder(this);
Ok(builder.__init_ok())
}
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __init_pin_internal {
(@named $builder:ident => $(,)?) => {};
(@named $builder:ident => $ident:ident : ? $expr:expr, $($tt:tt)*) => {
let $builder = match $builder.$ident($expr) {
Ok(v) => v,
Err(err) => return Err(err.map(From::from)),
};
__init_pin_internal!(@named $builder => $($tt)*);
};
(@named $builder:ident => $ident:ident : $expr:expr, $($tt:tt)*) => {
let $builder = match $builder.$ident($expr) {
Ok(v) => v,
Err(err) => return Err(err),
};
__init_pin_internal!(@named $builder => $($tt)*);
};
(@unnamed $builder:ident => $(,)?) => {};
(@unnamed $builder:ident => ? $expr:expr, $($tt:tt)*) => {
let $builder = match $builder.__next($expr) {
Ok(v) => v,
Err(err) => return Err(err.map(From::from)),
};
__init_pin_internal!(@unnamed $builder => $($tt)*);
};
(@unnamed $builder:ident => $expr:expr, $($tt:tt)*) => {
let $builder = match $builder.__next($expr) {
Ok(v) => v,
Err(err) => return Err(err),
};
__init_pin_internal!(@unnamed $builder => $($tt)*);
};
}