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

Checked Uninitialized Memory

Like C, all stack variables in Rust are uninitialized until a value is explicitly assigned to them. Unlike C, Rust statically prevents you from ever reading them until you do:

fn main() { let x: i32; println!("{}", x); }
fn main() {
    let x: i32;
    println!("{}", x);
}Run
src/main.rs:3:20: 3:21 error: use of possibly uninitialized variable: `x`
src/main.rs:3     println!("{}", x);
                                 ^

This is based off of a basic branch analysis: every branch must assign a value to x before it is first used. Interestingly, Rust doesn't require the variable to be mutable to perform a delayed initialization if every branch assigns exactly once. However the analysis does not take advantage of constant analysis or anything like that. So this compiles:

fn main() { let x: i32; if true { x = 1; } else { x = 2; } println!("{}", x); }
fn main() {
    let x: i32;

    if true {
        x = 1;
    } else {
        x = 2;
    }

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

but this doesn't:

fn main() { let x: i32; if true { x = 1; } println!("{}", x); }
fn main() {
    let x: i32;
    if true {
        x = 1;
    }
    println!("{}", x);
}Run
src/main.rs:6:17: 6:18 error: use of possibly uninitialized variable: `x`
src/main.rs:6   println!("{}", x);

while this does:

fn main() { let x: i32; if true { x = 1; println!("{}", x); } // Don't care that there are branches where it's not initialized // since we don't use the value in those branches }
fn main() {
    let x: i32;
    if true {
        x = 1;
        println!("{}", x);
    }
    // Don't care that there are branches where it's not initialized
    // since we don't use the value in those branches
}Run

Of course, while the analysis doesn't consider actual values, it does have a relatively sophisticated understanding of dependencies and control flow. For instance, this works:

fn main() { let x: i32; loop { // Rust doesn't understand that this branch will be taken unconditionally, // because it relies on actual values. if true { // But it does understand that it will only be taken once because // we unconditionally break out of it. Therefore `x` doesn't // need to be marked as mutable. x = 0; break; } } // It also knows that it's impossible to get here without reaching the break. // And therefore that `x` must be initialized here! println!("{}", x); }
let x: i32;

loop {
    // Rust doesn't understand that this branch will be taken unconditionally,
    // because it relies on actual values.
    if true {
        // But it does understand that it will only be taken once because
        // we unconditionally break out of it. Therefore `x` doesn't
        // need to be marked as mutable.
        x = 0;
        break;
    }
}
// It also knows that it's impossible to get here without reaching the break.
// And therefore that `x` must be initialized here!
println!("{}", x);Run

If a value is moved out of a variable, that variable becomes logically uninitialized if the type of the value isn't Copy. That is:

fn main() { let x = 0; let y = Box::new(0); let z1 = x; // x is still valid because i32 is Copy let z2 = y; // y is now logically uninitialized because Box isn't Copy }
fn main() {
    let x = 0;
    let y = Box::new(0);
    let z1 = x; // x is still valid because i32 is Copy
    let z2 = y; // y is now logically uninitialized because Box isn't Copy
}Run

However reassigning y in this example would require y to be marked as mutable, as a Safe Rust program could observe that the value of y changed:

fn main() { let mut y = Box::new(0); let z = y; // y is now logically uninitialized because Box isn't Copy y = Box::new(1); // reinitialize y }
fn main() {
    let mut y = Box::new(0);
    let z = y; // y is now logically uninitialized because Box isn't Copy
    y = Box::new(1); // reinitialize y
}Run

Otherwise it's like y is a brand new variable.