前言:
现在各位老铁们对“变量声明的规则”大体比较着重,你们都想要剖析一些“变量声明的规则”的相关内容。那么小编也在网上搜集了一些对于“变量声明的规则””的相关内容,希望各位老铁们能喜欢,大家快快来了解一下吧!我们在第3节已经用let关键字定义了一些基本类型的变量,也在九九乘法表程序中用println!宏打印了变量的值。在很多的语言里,变量的定义和使用规则都是比较直观的,并不需要作特别多的解释。但是Rust不同,Rust语言的设计者希望通过一些精心设计的数据(变量)使用的规则来保证内存安全。
接下来我们详细的了解一下Rust与其它语言最迥异的区别——围绕变量而生的一些约定。本节重点讲解变量的声明,下一节我们再来看变量的引用和所有权。
变量的定义:let关键字
我们已经知道所有的变量都需要先用let定义后才能使用。没有定义的变量会引起编译错误:
fn main() { let a = 10; // OK b = 20; // Error}
尝试编译上面的代码会得到这样的错误:
error[E0425]: cannot find value `b` in this scope --> var.rs:3:5 |3 | b = 20; | ^ help: a local variable with a similar name exists: `a`error: aborting due to previous errorFor more information about this error, try `rustc --explain E0425`.
在b = 20这一行加上let,就变成了合法的代码。
Rust还允许我们故意的“覆盖”一个变量。也就是通过重新let已有的变量名,让后续代码使用一个全新的变量,其类型甚至也可以跟之前不同。也就是说像这样的写法是可以通过的:
fn main() { let a = 10; println!("a is {}", a); let a = "hello"; println!("a is {}", a);}
上面的代码会有2行输出。上半部分的a是一个整数,下半部分被覆盖为一个字符串。
自然地,子代码块(例如条件/循环体)里的let也可以覆盖父作用域里存在的同名变量:
fn main() { let a = 10; println!("a is {}", a); for i in 1..10 { let a = "hello"; println!("a is {}", a); }}
let的一般形式都是在变量名后面加=,在声明的同时提供一个初始值。但是Rust也允许let只声明变量,而省略初始值。这样的变量,在使用前还是需要赋值,否则会出现编译错误。例如:
fn main() { let a; if 3 > 2 { a = "sane"; } else { a = "insane"; } println!("The world is {}.", a);}
上面的例子,实际上省掉了一个不必要且略显生硬的默认值。换句话说,假设我们写作let a = ""或者let a = "TBD",代码读起来都会有额外的理解负担。一则,初始值很快就被覆盖,所以并没有起到什么作用;再者,不论默认值是什么,都有点意图不明。
技术上来说,给所有的let都加一个有意义的初始值是可以做到的。前面提到过,if可以是一个有值的表达式,所以把整个逻辑糅合成let a = if ...这样的语法也是可以的。甚至,我们还可以使用代码块{}来包含更复杂的程序来计算初始值。但是,把声明放在单独的一行,有时候会使得程序结构更加清晰和简洁。
在有些语言里,这样做有可能造成使用未初始化的变量的bug,但是Rust的编译检查已经对这种情况做了判断,从而杜绝了忘记初始化变量的可能。
比如下面的代码尝试打印一个在编译器看来“可能”没有被初始化的变量:
fn main() { let a; if 3 > 2 { a = 1; } println!("a is {}", a);}
编译器会报出这样的错误:
error[E0381]: borrow of possibly-uninitialized variable: `a` --> var.rs:8:25 |8 | println!("a is {}", a); | ^ use of possibly-uninitialized `a`error: aborting due to previous errorFor more information about this error, try `rustc --explain E0381`.
当然,肉眼很容易看出3 > 2是一个恒为真的常量表达式,所以a = 1是一定会执行的,也就是println! 从程序逻辑上来讲,并不会访问未初始化的变量。这里仍然不能编译通过是因为目前的rustc(1.45.2)在编译期还没有对这种常量表达式的推导。
未使用的变量
如果你勤恳地编译了前面的一些示例代码,你可能已经注意到了,有时编译器会产生类似这样的警告:
warning: variable `a` is assigned to, but never used --> var.rs:2:9 |2 | let a; | ^ | = note: `#[warn(unused_variables)]` on by default = note: consider using `_a` instead
这是因为我们的变量在声明或者赋值以后,完全没有使用它的值。Rust希望通过这样的警告来避免无用代码,以保持程序整洁和可读。
有时出于语法需要,定义一些不被使用的变量是不可避免的。这些情况下,可以像警告里提示的那样,用一个简单的下划线_,或者一个以下划线_开头的变量名来告诉编译器“我知道这个变量的值确实不需要被用到,不用再警告我了”。
例如,这个写法可以用在for循环中:
fn main() { for _ in 1..10 { println!("hi there!"); }}
或者用在match的模式匹配中:
fn main() { let a = (0, 1, 2); let b = match a { (0, _y, z) => z, (1, y, _z) => y, (x, _y, _z) => x, }; println!("b is {}", b);}变量可变性:mut的使用
到现在为止,我们对变量只做过声明和初始化。如果你试着在初始化以后对变量的值进行修改,可能会得到下面的编译错误:
error[E0384]: cannot assign twice to immutable variable `a` --> var.rs:3:5 |2 | let a = 1; | - | | | first assignment to `a` | help: make this binding mutable: `mut a`3 | a = 2; | ^^^^^ cannot assign twice to immutable variableerror: aborting due to previous error; 1 warning emittedFor more information about this error, try `rustc --explain E0384`.
这是因为Rust跟很多其他的语言相反,用let声明的变量默认是“只读”的,也就是一旦赋予了初始值,就不能再进行改变。
这个设计的意图很明显,就是鼓励开发者尽可能地使用只读变量。这样做的好处也显而易见:一方面可以避免错误地对一个不该被修改的变量赋值引入bug,另一方面在阅读代码时,一个只读的数据比一个可能会改变的数据更容易把握。
为了声明一个货真价实的变量,我们需要用到mut关键字。把let改成let mut即可:
fn main() { let mut a = 1; println!("a is {}", a); a = 2; println!("a is {}", a);}
变量可以看作是一个值(或者说一段数据)的“代号”。数据才是实实在在存在于计算机里的,CPU做运算真正访问的东西。而是否有mut,并不改变变量的这一本质属性,它只决定了在语法层面,能否通过这一变量来修改其背后的数据。
这里需要注意,不可修改的变量跟我们通常理解的常量是不一样的。Rust里的常量是一个“字面上的值”,需要用const关键字来定义,并且必须指定类型:
const PI :f32 = 3.1416;fn main() { let r = 3f32; println!("Perimeter of a circle with radius of {} is {}", r, r * 2.0 * PI);}
换句话说,常量也可以看作一种代码字面(lexical)的“替换”,上面的代码可以等价于:
fn main() { let r = 3f32; println!("Perimeter of a circle with radius of {} is {}", r, r * 2.0 * 3.1416f32);}
最后我们照例用一个例子来结束今天的学习。
fn gcd(a :u32, b: u32) -> u32 { let max = |u, v| if u > v { u } else { v }; let min = |u, v| if u < v { u } else { v }; let mut x = max(a, b); let mut y = min(a, b); while x % y != 0 { let xx = x; x = y; y = xx % y } y}fn main() { let x = 320; let y = 88; println!("greatest common divisor of {} and {} is {}", x, y, gcd(x, y));}
关注红小豆,一起学习Rust开发。欢迎点赞,转发,收藏!
标签: #变量声明的规则