12-可变性

可变性 — Easy Rust 中文译本

译文 · 基于 Easy Rust

原文链接: https://dhghomon.github.io/easy_rust/Chapter_11.html

中文参考:kumakichi/easy_rust_chs

可变性

当你用let声明一个变量时,它是不可改变的(不能改变)。

这将无法工作:

1
2
3
4
fn main() {
    let my_number = 8;
    my_number = 10; // ⚠️
}

编译器说:error[E0384]: cannot assign twice to immutable variable my_number。这是因为如果你只写let,变量是不可变的。

但有时你想改变你的变量。要创建一个可以改变的变量,就在let后面加上mut。

1
2
3
4
fn main() {
    let mut my_number = 8;
    my_number = 10;
}

现在没有问题了。

但是,你不能改变类型:甚至mut也不能让你这样做:这将无法工作。

1
2
3
4
fn main() {
    let mut my_variable = 8; // 现在它是 i32。类型不能改
    my_variable = "Hello, world!"; // ⚠️
}

你会看到编译器发出的同样的 “预期"信息。expected integer, found &str. &str是一个字符串类型,我们很快就会知道。

遮蔽

shadowing是指使用let声明一个与另一个变量同名的新变量。它看起来像可变性,但完全不同。shadowing看起来是这样的:

1
2
3
4
5
6
fn main() {
    let my_number = 8; // 这是 i32
    println!("{}", my_number); // 打印 8
    let my_number = 9.2; // 这是同名的 f64。但不是第一个 my_number——完全是另一个!
    println!("{}", my_number) // 打印 9.2
}

这里我们说我们用一个新的 “let绑定"对my_number进行了 “shadowing”。

那么第一个my_number是否被销毁了呢?没有,但是当我们调用my_number时,我们现在得到my_number的f64。因为它们在同一个作用域块中(同一个 {}),我们不能再看到第一个 my_number。

但如果它们在不同的块中,我们可以同时看到两个。 例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn main() {
    let my_number = 8; // 这是 i32
    println!("{}", my_number); // 打印 8
    {
        let my_number = 9.2; // 这是 f64。不是原来的 my_number——完全是另一个!
        println!("{}", my_number) // 打印 9.2
                                  // 但被遮蔽的 my_number 只活到这里。
                                  // 第一个 my_number 还活着!
    }
    println!("{}", my_number); // 打印 8
}

因此,当你对一个变量进行shadowing处理时,你不会破坏它。你屏蔽了它。

那么shadowing的好处是什么呢?当你需要经常改变一个变量的时候,shadowing是很好的。想象一下,你想用一个变量做很多简单的数学运算。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fn times_two(number: i32) -> i32 {
    number * 2
}

fn main() {
    let final_number = {
        let y = 10;
        let x = 9; // x 从 9 开始
        let x = times_two(x); // 用新的 x 遮蔽:18
        let x = x + y; // 再用新的 x 遮蔽:28
        x // 返回 x:final_number 现在是 x 的值
    };
    println!("The number is now: {}", final_number)
}

如果没有shadowing,你将不得不考虑不同的名称,尽管你并不关心x。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn times_two(number: i32) -> i32 {
    number * 2
}

fn main() {
    // 假装我们在用不支持遮蔽的 Rust
    let final_number = {
        let y = 10;
        let x = 9; // x 从 9 开始
        let x_twice = times_two(x); // x 的第二个名字
        let x_twice_and_y = x_twice + y; // x 的第三个名字!
        x_twice_and_y // 可惜没有遮蔽——不然直接用 x 就行
    };
    println!("The number is now: {}", final_number)
}

一般来说,你在Rust中看到的shadowing就是这种情况。它发生在你想快速取用变量,对它做一些事情,然后再做其他事情的地方。而你通常将它用于那些你不太关心的快速变量。

最后修改 August 21, 2026: 更新 (76fc81a2e)