From 5e6973ddc5ba311ece0e2399eb2c2a49ef0d3620 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Sun, 2 Aug 2015 22:04:32 -0700 Subject: [PATCH] vastly expand on the mem::uninitialized docs --- src/libcore/mem.rs | 80 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 271d83201b1a5..3b321d43b3d11 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -253,21 +253,89 @@ pub unsafe fn dropped() -> T { dropped_impl() } -/// Creates an uninitialized value. +/// Bypasses Rust's normal memory-initialization checks by pretending to +/// produce a value of type T, while doing nothing at all. /// -/// Care must be taken when using this function, if the type `T` has a destructor and the value -/// falls out of scope (due to unwinding or returning) before being initialized, then the -/// destructor will run on uninitialized data, likely leading to crashes. +/// **This is incredibly dangerous, and should not be done lightly. Deeply +/// consider initializing your memory with a default value instead.** /// -/// This is useful for FFI functions sometimes, but should generally be avoided. +/// This is useful for FFI functions and initializing arrays sometimes, +/// but should generally be avoided. +/// +/// # Undefined Behaviour +/// +/// It is Undefined Behaviour to read uninitialized memory. Even just an +/// uninitialized boolean. For instance, if you branch on the value of such +/// a boolean your program may take one, both, or neither of the branches. +/// +/// Note that this often also includes *writing* to the uninitialized value. +/// Rust believes the value is initialized, and will therefore try to Drop +/// the uninitialized value and its fields if you try to overwrite the memory +/// in a normal manner. The only way to safely initialize an arbitrary +/// uninitialized value is with one of the `ptr` functions: `write`, `copy`, or +/// `copy_nonoverlapping`. This isn't necessary if `T` is a primitive +/// or otherwise only contains types that don't implement Drop. +/// +/// If this value *does* need some kind of Drop, it must be initialized before +/// it goes out of scope (and therefore would be dropped). Note that this +/// includes a `panic` occurring and unwinding the stack suddenly. /// /// # Examples /// +/// Here's how to safely initialize an array of `Vec`s. +/// /// ``` /// use std::mem; +/// use std::ptr; /// -/// let x: i32 = unsafe { mem::uninitialized() }; +/// // Only declare the array. This safely leaves it +/// // uninitialized in a way that Rust will track for us. +/// // However we can't initialize it element-by-element +/// // safely, and we can't use the `[value; 1000]` +/// // constructor because it only works with `Copy` data. +/// let mut data: [Vec; 1000]; +/// +/// unsafe { +/// // So we need to do this to initialize it. +/// data = mem::uninitialized(); +/// +/// // DANGER ZONE: if anything panics or otherwise +/// // incorrectly reads the array here, we will have +/// // Undefined Behaviour. +/// +/// // It's ok to mutably iterate the data, since this +/// // doesn't involve reading it at all. +/// // (ptr and len are statically known for arrays) +/// for elem in &mut data[..] { +/// // *elem = Vec::new() would try to drop the +/// // uninitialized memory at `elem` -- bad! +/// // +/// // Vec::new doesn't allocate or do really +/// // anything. It's only safe to call here +/// // because we know it won't panic. +/// ptr::write(elem, Vec::new()); +/// } +/// +/// // SAFE ZONE: everything is initialized. +/// } +/// +/// println!("{:?}", &data[0]); /// ``` +/// +/// Hopefully this example emphasizes to you exactly how delicate +/// and dangerous doing this is. Note that the `vec!` macro +/// *does* let you initialize every element with a value that +/// is only `Clone`, so the following is equivalent and vastly +/// less dangerous, as long as you can live with an extra heap +/// allocation: +/// +/// ``` +/// let data: Vec> = vec![Vec::new(); 1000]; +/// println!("{:?}", &data[0]); +/// ``` +/// +/// For large arrays this is probably advisable +/// anyway to avoid blowing the stack. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn uninitialized() -> T {