原文链接: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);
}
// 不关心其他的未初始化变量的分支
// 因为我们并不使用那些分支
}
当然,虽然分析过程不知道变量的实际值,它对依赖和控制流程的理解还是比较深入的。比如,这段代码是正确的:
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
会被视为一个全新的变量。