-
Notifications
You must be signed in to change notification settings - Fork 13.4k
MaybeUninit: add read_initialized, add examples #58660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
13ffbee
Add MaybeUninit::read_uninitialized
RalfJung dc570fb
also add examples to MaybeUninit::into_initialized
RalfJung 10f511d
misc tweaks
RalfJung 48aa59e
examples for as[_mut]_ptr
RalfJung 084ee7a
examples for MaybeUninit::zeroed
RalfJung d10366f
avoid unnecessary use of MaybeUninit::get_ref, and expand comment on …
RalfJung aa4a9b0
make MaybeUninit Copy
RalfJung 53c0275
Apply suggestions from code review
Centril ac2284b
expand type name
RalfJung 8ce9b86
fix link
RalfJung a5e2d0c
remark that the rules are unfinished
RalfJung be8d728
show how to set with ptr::write
RalfJung 6d32e5a
prefer into_initialized over read_initialited
RalfJung 90bb07e
Apply suggestions from code review
Centril cefe9b0
Apply suggestions from code review
RalfJung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1056,12 +1056,22 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> { | |
/// This is exploited by the compiler for various optimizations, such as eliding | ||
/// run-time checks and optimizing `enum` layout. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Not initializing memory at all (instead of zero-initializing it) causes the same | ||
/// issue: after all, the initial value of the variable might just happen to be | ||
/// one that violates the invariant. Moreover, uninitialized memory is special | ||
/// in that the compiler knows that it does not have a fixed value. This makes | ||
/// it undefined behavior to have uninitialized data in a variable even if that | ||
/// variable has otherwise no restrictions about which values are valid: | ||
/// Similarly, entirely uninitialized memory may have any content, while a `bool` must | ||
/// always be `true` or `false`. Hence, creating an uninitialized `bool` is undefined behavior: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::{self, MaybeUninit}; | ||
/// | ||
/// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior! | ||
/// // equivalent code with `MaybeUninit` | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// let b: bool = unsafe { MaybeUninit::uninitialized().into_initialized() }; // undefined behavior! | ||
/// ``` | ||
/// | ||
/// Moreover, uninitialized memory is special in that the compiler knows that | ||
/// it does not have a fixed value. This makes it undefined behavior to have | ||
/// uninitialized data in a variable even if that variable has integer type, | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// which otherwise can hold any bit pattern: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
|
@@ -1074,8 +1084,8 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> { | |
/// (Notice that the rules around uninitialized integers are not finalized yet, but | ||
/// until they are, it is advisable to avoid them.) | ||
/// | ||
/// `MaybeUninit` serves to enable unsafe code to deal with uninitialized data: | ||
/// it is a signal to the compiler indicating that the data here might *not* | ||
/// `MaybeUninit` serves to enable unsafe code to deal with uninitialized data. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// It is a signal to the compiler indicating that the data here might *not* | ||
/// be initialized: | ||
/// | ||
/// ```rust | ||
|
@@ -1092,16 +1102,26 @@ impl<T: ?Sized> DerefMut for ManuallyDrop<T> { | |
/// let x = unsafe { x.into_initialized() }; | ||
/// ``` | ||
/// | ||
/// The compiler then knows to not optimize this code. | ||
/// The compiler then knows to not make any incorrect assumptions or optimizations on this code. | ||
// FIXME before stabilizing, explain how to initialize a struct field-by-field. | ||
#[allow(missing_debug_implementations)] | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
// NOTE after stabilizing `MaybeUninit` proceed to deprecate `mem::{uninitialized,zeroed}` | ||
#[derive(Copy)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you remind me... what was the reason we didn't do this before? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think there was one. |
||
// NOTE after stabilizing `MaybeUninit` proceed to deprecate `mem::uninitialized` | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub union MaybeUninit<T> { | ||
uninit: (), | ||
value: ManuallyDrop<T>, | ||
} | ||
|
||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
impl<T: Copy> Clone for MaybeUninit<T> { | ||
#[inline(always)] | ||
fn clone(&self) -> Self { | ||
// Not calling T::clone(), we cannot know if we are initialized enough for that. | ||
*self | ||
} | ||
} | ||
|
||
impl<T> MaybeUninit<T> { | ||
/// Create a new `MaybeUninit` initialized with the given value. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
|
@@ -1131,6 +1151,35 @@ impl<T> MaybeUninit<T> { | |
/// | ||
/// Note that dropping a `MaybeUninit` will never call `T`'s drop code. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// It is your responsibility to make sure `T` gets dropped if it got initialized. | ||
/// | ||
/// # Example | ||
/// | ||
/// Correct usage of this method: initializing a struct with zero, where all | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// fields of the struct can hold 0 as a valid value. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// ```rust | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let x = MaybeUninit::<(u8, bool)>::zeroed(); | ||
/// let x = unsafe { x.into_initialized() }; | ||
/// assert_eq!(x, (0, false)); | ||
/// ``` | ||
/// | ||
/// *Incorrect* usage of this method: initializing a struct with zero, where some fields | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// cannot hold 0 as a valid value. | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// enum NotZero { One = 1, Two = 2 }; | ||
/// | ||
/// let x = MaybeUninit::<(u8, NotZero)>::zeroed(); | ||
/// let x = unsafe { x.into_initialized() }; | ||
/// // We create a `NotZero` (inside a pair) that does not have a valid discriminant. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// // This is undefined behavior. | ||
/// ``` | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
#[inline] | ||
pub fn zeroed() -> MaybeUninit<T> { | ||
|
@@ -1154,15 +1203,68 @@ impl<T> MaybeUninit<T> { | |
} | ||
|
||
/// Gets a pointer to the contained value. Reading from this pointer or turning it | ||
/// into a reference will be undefined behavior unless the `MaybeUninit` is initialized. | ||
/// into a reference is undefined behavior unless the `MaybeUninit` is initialized. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// # Examples | ||
/// | ||
/// Correct usage of this method: | ||
/// | ||
/// ```rust | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized(); | ||
/// x.set(vec![0,1,2]); | ||
/// // Create a reference into the `MaybeUninit`. This is okay because we initialized it. | ||
/// let x_vec = unsafe { &*x.as_ptr() }; | ||
/// assert_eq!(x_vec.len(), 3); | ||
/// ``` | ||
/// | ||
/// *Incorrect* usage of this method: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let x = MaybeUninit::<Vec<u32>>::uninitialized(); | ||
/// let x_vec = unsafe { &*x.as_ptr() }; | ||
/// // We have created a reference to an uninitialized vector! This is undefined behavior. | ||
/// ``` | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
#[inline(always)] | ||
pub fn as_ptr(&self) -> *const T { | ||
unsafe { &*self.value as *const T } | ||
} | ||
|
||
/// Gets a mutable pointer to the contained value. Reading from this pointer or turning it | ||
/// into a reference will be undefined behavior unless the `MaybeUninit` is initialized. | ||
/// into a reference is undefined behavior unless the `MaybeUninit` is initialized. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// # Examples | ||
/// | ||
/// Correct usage of this method: | ||
/// | ||
/// ```rust | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized(); | ||
/// x.set(vec![0,1,2]); | ||
/// // Create a reference into the `MaybeUninit`. This is okay because we initialized it. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// let x_vec = unsafe { &mut *x.as_mut_ptr() }; | ||
/// x_vec.push(3); | ||
/// assert_eq!(x_vec.len(), 4); | ||
/// ``` | ||
/// | ||
/// *Incorrect* usage of this method: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<Vec<u32>>::uninitialized(); | ||
/// let x_vec = unsafe { &mut *x.as_mut_ptr() }; | ||
/// // We have created a reference to an uninitialized vector! This is undefined behavior. | ||
/// ``` | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
#[inline(always)] | ||
pub fn as_mut_ptr(&mut self) -> *mut T { | ||
|
@@ -1178,13 +1280,95 @@ impl<T> MaybeUninit<T> { | |
/// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized | ||
/// state. Calling this when the content is not yet fully initialized causes undefined | ||
/// behavior. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Correct usage of this method: | ||
/// | ||
/// ```rust | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<bool>::uninitialized(); | ||
/// x.set(true); | ||
/// let x_init = unsafe { x.into_initialized() }; | ||
/// assert_eq!(x_init, true); | ||
/// ``` | ||
/// | ||
/// *Incorrect* usage of this method: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let x = MaybeUninit::<Vec<u32>>::uninitialized(); | ||
/// let x_init = unsafe { x.into_initialized() }; | ||
/// // `x` had not been initialized yet, so this last line causes undefined behavior. | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// ``` | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
#[inline(always)] | ||
pub unsafe fn into_initialized(self) -> T { | ||
intrinsics::panic_if_uninhabited::<T>(); | ||
ManuallyDrop::into_inner(self.value) | ||
} | ||
|
||
/// Reads the value from the `MaybeUninit` container. The resulting `T` is subject | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// to the usual drop handling. | ||
/// | ||
/// # Unsafety | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// It is up to the caller to guarantee that the `MaybeUninit` really is in an initialized | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// state. Calling this when the content is not yet fully initialized causes undefined | ||
/// behavior. | ||
/// | ||
/// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using | ||
RalfJung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// multiple copies of the data (by calling `read_initialized` multiple times, or first | ||
/// calling `read_initialized` and then [`into_initialized`]), it is your responsibility | ||
/// to ensure that that data may indeed be duplicated. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Correct usage of this method: | ||
/// | ||
/// ```rust | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<u32>::uninitialized(); | ||
/// x.set(13); | ||
/// let x1 = unsafe { x.read_initialized() }; | ||
/// // `u32` is `Copy`, so we may read multiple times. | ||
/// let x2 = unsafe { x.read_initialized() }; | ||
/// assert_eq!(x1, x2); | ||
/// | ||
/// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninitialized(); | ||
/// x.set(None); | ||
/// let x1 = unsafe { x.read_initialized() }; | ||
/// // Duplicating a `None` value is okay, so we may read multiple times. | ||
/// let x2 = unsafe { x.read_initialized() }; | ||
/// assert_eq!(x1, x2); | ||
/// ``` | ||
/// | ||
/// *Incorrect* usage of this method: | ||
/// | ||
/// ```rust,no_run | ||
/// #![feature(maybe_uninit)] | ||
/// use std::mem::MaybeUninit; | ||
/// | ||
/// let mut x = MaybeUninit::<Option<Vec<u32>>>::uninitialized(); | ||
/// x.set(Some(vec![0,1,2])); | ||
/// let x1 = unsafe { x.read_initialized() }; | ||
/// let x2 = unsafe { x.read_initialized() }; | ||
/// // We now created two copies of the same vector, leading to a double-free when | ||
/// // they both get dropped! | ||
/// ``` | ||
#[unstable(feature = "maybe_uninit", issue = "53491")] | ||
#[inline(always)] | ||
pub unsafe fn read_initialized(&self) -> T { | ||
intrinsics::panic_if_uninhabited::<T>(); | ||
self.as_ptr().read() | ||
} | ||
|
||
/// Gets a reference to the contained value. | ||
/// | ||
/// # Unsafety | ||
|
This file contains hidden or 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.