讲动人的故事,写懂人的代码
3.2 变量绑定的声明和初始化分开
在3.1.1中提到,变量的声明和初始化可以分开。而这也为程序员挖了一个坑,如代码清单3-4所示。
本书代码下载链接为github.com/wubin28/book_LRBACP。本书所有的代码清单,会注明在这个链接中的文件夹位置,以便读者找到相应的没有行号的代码来运行。
下载代码之前,请先安装git。具体的安装步骤,可以询问你最喜欢用的生成式AI聊天工具。
之后,可以运行git clone
命令,然后进入文件夹book_LRBACP
即能看到所有代码。
代码清单3-4 在初始化完成之前误用变量
rust
// 源代码位置:ch03/uninitialized_peril
1 fn main() {
3 let x: i32;
4 if true {
5 x = 5;
6 }
7 // 取消注释下面一行以查看编译错误: 变量`x`在某些执行路径上未初始化
8 // println!("x的值是: {}", x);
18 }
代码清单3-4所对应的完整源代码,展示了"在初始化完成之前误用变量"的问题,以及如何正确初始化变量。代码分为两部分:一个有潜在问题的部分和一个修复后的部分,都涉及变量的声明和初始化。
第3行声明了一个i32
类型的变量x
,但没有立即初始化它。
第4-6行:在一个if
表达式中初始化x
。虽然这里条件始终为true
,x
总会被赋值为5
,但编译器却不放过这一点。
第8行被注释掉了。如果取消注释,就会踩坑里。这将导致编译错误,因为编译器认为x
在某些执行路径上未初始化。
❗️变量初始化避坑指南
当变量的声明与初始化分开,并使用if
表达式进行初始化时,如果无法确保在所有可能的执行路径上都初始化变量,那么编译会报错。
如何修复这个问题?只要在第6行后面加个else
表达式,并在表达式里给x
再初始化一下,让编译器确信所有执行路径都初始化即可。
3.3 多次绑定同一名称进行变量遮蔽
Rust中的变量遮蔽(variable shadowing),指在同一作用域内使用相同名称声明新变量的能力。新变量会"遮蔽"之前声明的同名变量,之前的变量不再有效。这允许程序员重用变量名。如下所示。
rust
fn main() {
let x = 5;
let x = x + 1; // 新的x遮蔽了之前的x
println!("x is {}", x);
}
// 输出:
// x is 6
这个特性在保持变量名简洁的同时,允许程序员在同一作用域的不同阶段,针对同一名称声明不同类型、值或可变性的新变量。需要注意的是,遮蔽和可变性(mutability)是不同的概念。
❗️变量遮蔽避坑指南
遮蔽创建了一个新变量,而非修改旧变量。这个新变量虽与旧变量同名,但可能拥有完全不同的类型、值或可变性。
变量遮蔽虽然有重用变量名的便利,但若使用不当,会让程序员踩什么坑?主要会踩3个坑。
第1个坑是忘记遮蔽更改了变量类型。变量遮蔽能创建不同类型的新变量,但粗心的程序员有时会忘记这一点,继续使用遮蔽前的类型而踩坑(踩坑源代码参见:ch03/shadow_type_amnesia)。
第2个坑是多次更改变量含义的遮蔽,引发困惑。变量遮蔽的本质,是相同的名称来声明新的变量。新的变量的含义在每次声明时,难免会发生变化。当变量的含义变化得过大时,变量名就变成错误的"指路牌",往往让程序员产生误解而踩坑(踩坑源代码参见:ch03/shadowing_maze)。
第3个坑是忘记遮蔽更改了变量可变性。这和第1个坑有点像。在用变量遮蔽绑定新的同名变量时,可以更改变量的可变性。粗心的程序员会忘记这一点而踩坑(踩坑源代码参见:ch03/shadowed_mutability_trap)。
如果喜欢这篇文章,别忘了给文章点个"在看",好鼓励小吾继续写哦~😃