]> git.lizzy.rs Git - rust.git/blob - src/doc/tarpl/checked-uninit.md
706016a480c6625c9362c41876418197687dff12
[rust.git] / src / doc / tarpl / checked-uninit.md
1 % Checked Uninitialized Memory
2
3 Like C, all stack variables in Rust are uninitialized until a value is
4 explicitly assigned to them. Unlike C, Rust statically prevents you from ever
5 reading them until you do:
6
7 ```rust,ignore
8 fn main() {
9     let x: i32;
10     println!("{}", x);
11 }
12 ```
13
14 ```text
15 src/main.rs:3:20: 3:21 error: use of possibly uninitialized variable: `x`
16 src/main.rs:3     println!("{}", x);
17                                  ^
18 ```
19
20 This is based off of a basic branch analysis: every branch must assign a value
21 to `x` before it is first used. Interestingly, Rust doesn't require the variable
22 to be mutable to perform a delayed initialization if every branch assigns
23 exactly once. However the analysis does not take advantage of constant analysis
24 or anything like that. So this compiles:
25
26 ```rust
27 fn main() {
28     let x: i32;
29
30     if true {
31         x = 1;
32     } else {
33         x = 2;
34     }
35
36     println!("{}", x);
37 }
38 ```
39
40 but this doesn't:
41
42 ```rust,ignore
43 fn main() {
44     let x: i32;
45     if true {
46         x = 1;
47     }
48     println!("{}", x);
49 }
50 ```
51
52 ```text
53 src/main.rs:6:17: 6:18 error: use of possibly uninitialized variable: `x`
54 src/main.rs:6   println!("{}", x);
55 ```
56
57 while this does:
58
59 ```rust
60 fn main() {
61     let x: i32;
62     if true {
63         x = 1;
64         println!("{}", x);
65     }
66     // Don't care that there are branches where it's not initialized
67     // since we don't use the value in those branches
68 }
69 ```
70
71 Of course, while the analysis doesn't consider actual values, it does
72 have a relatively sophisticated understanding of dependencies and control
73 flow. For instance, this works:
74
75 ```rust
76 let x: i32;
77
78 loop {
79     // Rust doesn't understand that this branch will be taken unconditionally,
80     // because it relies on actual values.
81     if true {
82         // But it does understand that it will only be taken once because
83         // we *do* unconditionally break out of it. Therefore `x` doesn't
84         // need to be marked as mutable.
85         x = 0;
86         break;
87     }
88 }
89 // It also knows that it's impossible to get here without reaching the break.
90 // And therefore that `x` must be initialized here!
91 println!("{}", x);
92 ```
93
94 If a value is moved out of a variable, that variable becomes logically
95 uninitialized if the type of the value isn't Copy. That is:
96
97 ```rust
98 fn main() {
99     let x = 0;
100     let y = Box::new(0);
101     let z1 = x; // x is still valid because i32 is Copy
102     let z2 = y; // y is now logically uninitialized because Box isn't Copy
103 }
104 ```
105
106 However reassigning `y` in this example *would* require `y` to be marked as
107 mutable, as a Safe Rust program could observe that the value of `y` changed:
108
109 ```rust
110 fn main() {
111     let mut y = Box::new(0);
112     let z = y; // y is now logically uninitialized because Box isn't Copy
113     y = Box::new(1); // reinitialize y
114 }
115 ```
116
117 Otherwise it's like `y` is a brand new variable.