-
-
Save WorldSEnder/e6d4bebe8cc5a03c84c791ec81712c7f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use core::mem::ManuallyDrop; | |
use core::ops::DerefMut; | |
use core::ops::Deref; | |
use core::mem::MaybeUninit; | |
/// Represents an initialized MaybeUninit. The contained value is dropped with this | |
/// evidence. | |
/// | |
/// Safety: The referenced MaybeUninit is initialized | |
struct SurelyInit<'u, T>(&'u mut MaybeUninit<T>); | |
impl<'u, T> SurelyInit<'u, T> { | |
/// Writes a value into the MaybeUninit and returns evidence of initialization. | |
/// Any previous value is overwritten without its drop impl being called. | |
fn init(this: &'u mut MaybeUninit<T>, value: T) -> Self { | |
this.write(value); | |
// Safety: the MaybeUninit has been initialized. | |
unsafe { Self::assume_init(this) } | |
} | |
/// Safety: must ensure that the MaybeUninit is initialized | |
unsafe fn assume_init(this: &'u mut MaybeUninit<T>) -> Self { | |
// Safety: by the caller contract | |
Self(this) | |
} | |
/// Reads the value from the underlying MaybeUninit and "deinitializes" it. | |
fn take(self) -> T { | |
// We will shortly replace the contents destructively. Make sure no drop happens | |
// on the uninitialized replacement | |
let mut this = ManuallyDrop::new(self); | |
// Allow the compiler to replace the old value with any garbage. | |
// Subtly, this also allows the compiler to omit the copy all together and overwrite | |
// and destructively consume whatever value exists here | |
let old = std::mem::replace(this.0, MaybeUninit::uninit()); | |
// Safety: The old value is assumed to have been initialized | |
// The value read is not dropped twice. | |
unsafe { old.assume_init() } | |
} | |
} | |
impl<'u, T> Deref for SurelyInit<'u, T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
unsafe { self.0.assume_init_ref() } | |
} | |
} | |
impl<'u, T> DerefMut for SurelyInit<'u, T> { | |
fn deref_mut(&mut self) -> &mut T { | |
unsafe { self.0.assume_init_mut() } | |
} | |
} | |
impl<'u, T> Drop for SurelyInit<'u, T> { | |
fn drop(&mut self) { | |
unsafe { self.0.assume_init_drop() } | |
} | |
} | |
pub fn main() { | |
let mut place: MaybeUninit<u32> = MaybeUninit::uninit(); | |
{ | |
let mut init = SurelyInit::init(&mut place, 42); | |
assert_eq!(*init, 42); | |
*init += 10; | |
assert_eq!(init.take(), 52); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment