在 Rust 中,"shadowing" 和 "mutability" 有什么区别?
In Rust, what's the difference between "shadowing" and "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 对阴影 变量的处理,我们可以简单地这样做来更改尽管如此 "immutable" 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 部分):
It’s important that we get compile-time errors when we attempt to
change a value that we previously designated as immutable because this
very situation can lead to bugs. If one part of our code operates on
the assumption that a value will never change and another part of our
code changes that value, it’s possible that the first part of the code
won’t do what it was designed to do. The cause of this kind of bug can
be difficult to track down after the fact, especially when the second
piece of code changes the value only sometimes.
In Rust, the compiler guarantees that when you state that a value
won’t change, it really won’t change. That means that when you’re
reading and writing code, you don’t have to keep track of how and
where a value might change. Your code is thus easier to reason
through.
如果我可以通过对 let
的足够无辜的调用来回避我的不可变 x
的这一重要特性,为什么我需要 mut
?有没有什么方法可以让 x
变得不可变,这样就没有 let x
可以重新分配它的值了?
我认为混淆是因为您将名称与存储混为一谈。
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_0
和x_1
)。第二个 let
只是重新绑定名称 x
以引用存储位置 x_1
。 x_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
之类的东西来拒绝阴影作为 lint。
在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 对阴影 变量的处理,我们可以简单地这样做来更改尽管如此 "immutable" 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 部分):
It’s important that we get compile-time errors when we attempt to change a value that we previously designated as immutable because this very situation can lead to bugs. If one part of our code operates on the assumption that a value will never change and another part of our code changes that value, it’s possible that the first part of the code won’t do what it was designed to do. The cause of this kind of bug can be difficult to track down after the fact, especially when the second piece of code changes the value only sometimes.
In Rust, the compiler guarantees that when you state that a value won’t change, it really won’t change. That means that when you’re reading and writing code, you don’t have to keep track of how and where a value might change. Your code is thus easier to reason through.
如果我可以通过对 let
的足够无辜的调用来回避我的不可变 x
的这一重要特性,为什么我需要 mut
?有没有什么方法可以让 x
变得不可变,这样就没有 let x
可以重新分配它的值了?
我认为混淆是因为您将名称与存储混为一谈。
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_0
和x_1
)。第二个 let
只是重新绑定名称 x
以引用存储位置 x_1
。 x_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
之类的东西来拒绝阴影作为 lint。