原文链接:https://doc.rust-lang.org/nomicon/checked-uninit.html
安全方式
和C一样,所有栈上的变量在显式赋值之前都是未初始化的。而和C不同的是,Rust禁止你在赋值之前读取它们:
fn main() { let x: i32; println!("{}", x); }
src/main.rs:3:20: 3:21 error: use of possibly uninitialized variable: `x`
src/main.rs:3 println!("{}", x);
^
这个错误基于分支分析:任何一个分支在第一次使用x
之前都必须对它赋值。有意思的是,如果每一个分支都只赋值一次的话,Rust并不要求变量是可变的。但是,这个分析过程没有配合常量分析。所以下面这段代码可以编译:
fn main() { let x: i32; if true { x = 1; } else { x = 2; } println!("{}", x); }
但是这段却不能编译:
fn main() { let x: i32; if true { x = 1; } println!("{}", x); }
src/main.rs:6:17: 6:18 error: use of possibly uninitialized variable: `x`
src/main.rs:6 println!("{}", x);
而这一段又可以编译:
fn main() { let x: i32; if true { x = 1; println!("{}", x); } // 不关心其他的未初始化变量的分支 // 因为我们并不使用那些分支 }
当然,虽然分析过程不知道变量的实际值,它对依赖和控制流程的理解还是比较深入的。比如,这段代码是正确的:
#![allow(unused)] fn main() { let x: i32; loop { // Rust不知道这个分支会被无条件执行 //因为它依赖于实际值 if true { // 但是它确实知道循环只会有一次,因为我们会无条件break // 所以x不需要是可变的 x = 0; break; } } // 它也知道如果没有执行break的话,代码不会运行到这里 // 所以在这里x一定已经被初始化了 println!("{}", x); }
如果值从变量中移出且变量类型不是Copy,那么变量逻辑上处于未初始化状态。就是说:
fn main() { let x = 0; let y = Box::new(0); let z1 = x; // x仍然是合法的,因为i32是Copy let z2 = y; // y现在逻辑上未初始化,因为Box不是Copy }
但是,这个例子中对y
重新赋值要求y
是可变的,因为安全Rust能够观察到y
的值发生了变化:
fn main() { let mut y = Box::new(0); let z = y; // y现在逻辑上未初始化,因为Box不是Copy y = Box::new(1); // 重新初始化y }
否则y
会被视为一个全新的变量。