可变性

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

这将无法工作:

fn main() {
    let my_number = 8;
    my_number = 10; // ⚠️
}

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

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

fn main() {
    let mut my_number = 8;
    my_number = 10;
}

现在没有问题了。

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

fn main() {
    let mut my_variable = 8; // it is now an i32. That can't be changed
    my_variable = "Hello, world!"; // ⚠️
}

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

遮蔽

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

fn main() {
    let my_number = 8; // This is an i32
    println!("{}", my_number); // prints 8
    let my_number = 9.2; // This is an f64 with the same name. But it's not the first my_number - it is completely different!
    println!("{}", my_number) // Prints 9.2
}

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

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

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

fn main() {
    let my_number = 8; // This is an i32
    println!("{}", my_number); // prints 8
    {
        let my_number = 9.2; // This is an f64. It is not my_number - it is completely different!
        println!("{}", my_number) // Prints 9.2
                                  // But the shadowed my_number only lives until here.
                                  // The first my_number is still alive!
    }
    println!("{}", my_number); // prints 8
}

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

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

fn times_two(number: i32) -> i32 {
    number * 2
}

fn main() {
    let final_number = {
        let y = 10;
        let x = 9; // x starts at 9
        let x = times_two(x); // shadow with new x: 18
        let x = x + y; // shadow with new x: 28
        x // return x: final_number is now the value of x
    };
    println!("The number is now: {}", final_number)
}

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

fn times_two(number: i32) -> i32 {
    number * 2
}

fn main() {
    // Pretending we are using Rust without shadowing
    let final_number = {
        let y = 10;
        let x = 9; // x starts at 9
        let x_twice = times_two(x); // second name for x
        let x_twice_and_y = x_twice + y; // third name for x!
        x_twice_and_y // too bad we didn't have shadowing - we could have just used x
    };
    println!("The number is now: {}", final_number)
}

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