Rust 的变量和可变性

424 阅读4分钟
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

保存并使用 cargo run运行程序。应该会看到一条错误信息,如下输出所示: 错误的原因是 不能对不可变变量 x 二次赋值 (cannot assign twice to immutable variable x),因为你尝试对不可变变量 x 赋第二个值。

如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 有时 会改变值。

Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导。

不过可变性也是非常有用的。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 mut来使其可变。除了允许改变值之外,mut 向读者表明了其他代码将会改变这个变量值的意图。

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

例如,让我们将 src/main.rs 修改为如下代码:

fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

通过 mut,允许把绑定到x的值从5改成6。在一些情况下,你会想用可变变量,因为与只用不可变变量相比,它会让代码更容易编写。

变量和常量的区别

不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:常量(constants)。类似于不可变变量,常量是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。

首先,不允许对常量使用mut。常量不光默认不能变,它总是不能变。

声明常量使用 const关键字而不是let,并且必须注明值的类型。在下一部分,“数据类型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。

常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。

最后一个区别是,常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值。

隐藏(Shadowing)

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;

    println!("The value of x is: {}", x);
}

最后能执行成功:The value of x is: 12

What !!!???

这个程序首先将 x 绑定到值 5 上。接着通过 let x = 隐藏 x,获取初始值并加 1,这样 x 的值就变成 6 了。第三个 let 语句也隐藏了x,将之前的值乘以 2,x 最终的值是 12。

隐藏与 将变量标记为mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,就会导致编译时错误。通过使用let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。

mut与隐藏的另一个区别是,当再次使用let时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,然而我们真正需要的是将输入存储成数字(多少个空格):

fn main() {
    let spaces = "   ";
    let spaces = spaces.len();
}

这里允许第1个 spaces 变量是字符串类型,而第2个 spaces 变量,它是一个恰巧与第1个变量同名的新的变量,是数字类型。 隐藏使我们不必使用不同的名字,如 spaces_strspaces_num; 相反,我们可以复用 spaces 这个更简单的名字。然而,如果尝试用 mut,将会得到一个编译时错误,如下所示:

let mut spaces = "   ";
spaces = spaces.len();

这个错误说明,我们不能改变变量的类型

error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected &str, found usize
  |
  = note: expected type `&str`
             found type `usize`

参考:

kaisery.github.io/trpl-zh-cn/…