在Rust中,“阴影”和“可变性”有什么区别?

时间:2018-11-10 01:45:16

标签: variables rust immutability mutability

Chapter 3 of the Rust Book变量和可变性中,我们对该主题进行了两次迭代,以演示Rust中变量的默认,不可变行为:

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

哪个输出:

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

但是,由于Rust使用 shadowing 变量,我们可以简单地执行此操作以更改“不可变” x的值:

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

哪些输出(跳过详细信息):

The value of x is 5
The value of x is 6

很有趣的是,尽管我们不调用let而是第一次mut绑定到x的事实,但是这段代码也会产生上述两行作为输出5

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

这种如何(不是真的)保护变量不被重新分配的模棱两可似乎违背了既定目标,即保护绑定到不可变的值(Rust默认情况下)的变量。在同一章中(还包括 Shadowing 部分):

  

重要的是,当我们尝试进行编译时会出现编译时错误   更改我们先前指定为不可变的值,因为这   这种情况可能会导致错误。如果我们的代码的一部分在   价值永远不变的假设,以及我们的另一部分   代码更改了该值,代码的第一部分可能   不会按照原计划做。这种错误的原因可以   事后很难追踪,尤其是当第二次   一段代码仅有时会更改值。

     

在Rust中,编译器保证在您声明该值时   不会改变,它真的不会改变。这意味着当你   阅读和编写代码,您不必跟踪如何以及   值可能会改变的地方。因此,您的代码更容易推理   通过。

如果我可以通过无害地调用x来使不可变的let的这一重要功能回避,为什么我需要mut?是否有某种方法可以使您认真地使x变得不可变,从而使let x不能重新分配其值?

1 个答案:

答案 0 :(得分:12)

我相信造成混淆的原因是,您正在将名称与存储空间相混淆。

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

在此示例中,有一个名称(x)和两个存储位置(x_0x_1)。第二个let只是重新绑定了名称x以引用存储位置x_1x_0的存储位置完全不受影响。

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

在此示例中,有一个名称(x)和一个存储位置(x_0)。 x = 6分配直接更改了存储位置x_0的位。

您可能会争辩说,它们做同样的事情。如果是这样,那你就错了:

fn main() {
    let x = 5; // x_0
    let y = &x; // y_0
    println!("The value of y is {}", y);
    let x = 6; // x_1
    println!("The value of y is {}", y);
}

这将输出:

The value of y is 5
The value of y is 5

这是因为更改x所指向的存储位置绝对不会对x_0所包含的指针的存储位置y_0产生影响。但是,

fn main() {
    let mut x = 5; // x_0
    let y = &x; // y_0
    println!("The value of y is {}", y);
    x = 6;
    println!("The value of y is {}", y);
}

无法编译,因为借用x_0时无法对其进行突变。

Rust关心通过参考文献观察到的免受不必要的突变影响。这与允许阴影没有冲突,因为在阴影时您不会更改值,而只是以一种在其他任何地方都无法观察到的方式更改特定名称的含义。阴影完全是局部变化。

因此,是的,您绝对可以避免更改x的值。您不能要做的是不要更改名称x所指的内容。最多,您可以使用clippy之类的工具来抹去阴影。