1. 1. Introduction
  2. 2. Meet Safe and Unsafe
    1. 2.1. How Safe and Unsafe Interact
    2. 2.2. Working with Unsafe
  3. 3. Data Layout
    1. 3.1. repr(Rust)
    2. 3.2. Exotically Sized Types
    3. 3.3. Other reprs
  4. 4. Ownership
    1. 4.1. References
    2. 4.2. Lifetimes
    3. 4.3. Limits of Lifetimes
    4. 4.4. Lifetime Elision
    5. 4.5. Unbounded Lifetimes
    6. 4.6. Higher-Rank Trait Bounds
    7. 4.7. Subtyping and Variance
    8. 4.8. Drop Check
    9. 4.9. PhantomData
    10. 4.10. Splitting Borrows
  5. 5. Type Conversions
    1. 5.1. Coercions
    2. 5.2. The Dot Operator
    3. 5.3. Casts
    4. 5.4. Transmutes
  6. 6. Uninitialized Memory
    1. 6.1. Checked
    2. 6.2. Drop Flags
    3. 6.3. Unchecked
  7. 7. Ownership Based Resource Management
    1. 7.1. Constructors
    2. 7.2. Destructors
    3. 7.3. Leaking
  8. 8. Unwinding
    1. 8.1. Exception Safety
    2. 8.2. Poisoning
  9. 9. Concurrency
    1. 9.1. Races
    2. 9.2. Send and Sync
    3. 9.3. Atomics
  10. 10. Implementing Vec
    1. 10.1. Layout
    2. 10.2. Allocating
    3. 10.3. Push and Pop
    4. 10.4. Deallocating
    5. 10.5. Deref
    6. 10.6. Insert and Remove
    7. 10.7. IntoIter
    8. 10.8. RawVec
    9. 10.9. Drain
    10. 10.10. Handling Zero-Sized Types
    11. 10.11. Final Code
  11. 11. Implementing Arc and Mutex

Unchecked Uninitialized Memory

One interesting exception to this rule is working with arrays. Safe Rust doesn't permit you to partially initialize an array. When you initialize an array, you can either set every value to the same thing with let x = [val; N], or you can specify each member individually with let x = [val1, val2, val3]. Unfortunately this is pretty rigid, especially if you need to initialize your array in a more incremental or dynamic way.

Unsafe Rust gives us a powerful tool to handle this problem: mem::uninitialized. This function pretends to return a value when really it does nothing at all. Using it, we can convince Rust that we have initialized a variable, allowing us to do trickier things with conditional and incremental initialization.

Unfortunately, this opens us up to all kinds of problems. Assignment has a different meaning to Rust based on whether it believes that a variable is initialized or not. If it's believed uninitialized, then Rust will semantically just memcopy the bits over the uninitialized ones, and do nothing else. However if Rust believes a value to be initialized, it will try to Drop the old value! Since we've tricked Rust into believing that the value is initialized, we can no longer safely use normal assignment.

This is also a problem if you're working with a raw system allocator, which returns a pointer to uninitialized memory.

To handle this, we must use the ptr module. In particular, it provides three functions that allow us to assign bytes to a location in memory without dropping the old value: write, copy, and copy_nonoverlapping.

It should go without saying that these functions, if misused, will cause serious havoc or just straight up Undefined Behavior. The only things that these functions themselves require is that the locations you want to read and write are allocated. However the ways writing arbitrary bits to arbitrary locations of memory can break things are basically uncountable!

Putting this all together, we get the following:

use std::mem;
use std::ptr;

// size of the array is hard-coded but easy to change. This means we can't
// use [a, b, c] syntax to initialize the array, though!
const SIZE: usize = 10;

let mut x: [Box<u32>; SIZE];

unsafe {
    // convince Rust that x is Totally Initialized
    x = mem::uninitialized();
    for i in 0..SIZE {
        // very carefully overwrite each index without reading it
        // NOTE: exception safety is not a concern; Box can't panic
        ptr::write(&mut x[i], Box::new(i as u32));
    }
}

println!("{:?}", x);Run

It's worth noting that you don't need to worry about ptr::write-style shenanigans with types which don't implement Drop or contain Drop types, because Rust knows not to try to drop them. Similarly you should be able to assign to fields of partially initialized structs directly if those fields don't contain any Drop types.

However when working with uninitialized memory you need to be ever-vigilant for Rust trying to drop values you make like this before they're fully initialized. Every control path through that variable's scope must initialize the value before it ends, if it has a destructor. This includes code panicking.

And that's about it for working with uninitialized memory! Basically nothing anywhere expects to be handed uninitialized memory, so if you're going to pass it around at all, be sure to be really careful.