"if" 语句在 Rust 程序中条件为 "false" 时执行,如何理解?
"if" statement gets executed when the condition is "false" in a Rust program, how to understand it?
对于以下 Rust 程序:
fn main() {
let foo = "test".to_string();
if false {
let _bar = foo; // value moved to _bar
}
println!("{}", foo);
}
我在 运行 时收到此错误:
error[E0382]: borrow of moved value: `foo`
--> src\main.rs:6:20
|
2 | let foo = "test".to_string();
| --- move occurs because `foo` has type `std::string::String`, which does not implement the `Copy` trait
3 | if false {
4 | let _bar = foo; // value moved to _bar
| --- value moved here
5 | }
6 | println!("{}", foo);
| ^^^ value borrowed here after move
谁能帮忙解释一下这里发生了什么? move 发生在 if 语句中,这对我来说很奇怪,这永远不会是真的。另外我想了解一下这种情况,应该用什么关键词搜索?
移动的秘密:它们并不真正存在。
移动不会生成与按位复制不同的代码(在机器代码的意义上)。¹移动和复制之间的唯一区别是 "original" 会发生什么:如果它仍然有效,它是一个副本;如果原来的不再有效,那就是移动。
那么编译器如何强制您在移动后不使用原始值?没有运行时标志来跟踪 foo
是否有效。² 相反,编译器使用源代码分析在编译时确定 foo
是否绝对有效或可能已被移出当您尝试使用它时。因为这种分析发生在编译时,所以它不遵循函数内的执行流程;它会同时发生在整个函数中。编译器发现 foo
被移出了 if
内部,并在不评估条件或任何代码的情况下拒绝稍后使用 foo
。
智能编译器可以在进行有效性分析时考虑控制流,³但这可能不是改进。并不总是能够知道一个分支是否被采用(它是 undecidable),所以在某些情况下编译器仍然会出错。此外,正如 Cerberus 在问题评论中指出的那样,它会大大减慢编译器的速度。
换句话说:在 Rust 中,你永远不会显式移动某些东西。你想怎么弄就怎么弄,让编译器根据类型是不是Copy
,后面有没有用到,告诉你做错了没有。这与 C++ 不同,在 C++ 中,移动是一个可能调用 "move constructor" 并有副作用的操作;在 Rust 中,它是一个纯静态的 pass/fail 检查。如果你做对了,程序就会通过并进入下一阶段的编译;如果您做错了,借用检查器会告诉您(并希望能帮助您解决问题)。
另见
- Runtime vs Compile time
¹ 除非移动类型实现 Drop
,在这种情况下,编译器可能会发出 drop flags.
² 实际上,是(丢弃标志),但它只在 foo
被丢弃时检查,而不是在每次使用时检查。不实现 Drop
的类型没有丢弃标志,即使它们具有相同的移动语义。
³ 这类似于空检查在 Kotlin 中的工作方式:如果编译器可以确定引用肯定是非空的,它将允许您取消引用它。 Rust 中的有效性分析比这更保守;编译器甚至不会尝试猜测。
对于以下 Rust 程序:
fn main() {
let foo = "test".to_string();
if false {
let _bar = foo; // value moved to _bar
}
println!("{}", foo);
}
我在 运行 时收到此错误:
error[E0382]: borrow of moved value: `foo`
--> src\main.rs:6:20
|
2 | let foo = "test".to_string();
| --- move occurs because `foo` has type `std::string::String`, which does not implement the `Copy` trait
3 | if false {
4 | let _bar = foo; // value moved to _bar
| --- value moved here
5 | }
6 | println!("{}", foo);
| ^^^ value borrowed here after move
谁能帮忙解释一下这里发生了什么? move 发生在 if 语句中,这对我来说很奇怪,这永远不会是真的。另外我想了解一下这种情况,应该用什么关键词搜索?
移动的秘密:它们并不真正存在。
移动不会生成与按位复制不同的代码(在机器代码的意义上)。¹移动和复制之间的唯一区别是 "original" 会发生什么:如果它仍然有效,它是一个副本;如果原来的不再有效,那就是移动。
那么编译器如何强制您在移动后不使用原始值?没有运行时标志来跟踪 foo
是否有效。² 相反,编译器使用源代码分析在编译时确定 foo
是否绝对有效或可能已被移出当您尝试使用它时。因为这种分析发生在编译时,所以它不遵循函数内的执行流程;它会同时发生在整个函数中。编译器发现 foo
被移出了 if
内部,并在不评估条件或任何代码的情况下拒绝稍后使用 foo
。
智能编译器可以在进行有效性分析时考虑控制流,³但这可能不是改进。并不总是能够知道一个分支是否被采用(它是 undecidable),所以在某些情况下编译器仍然会出错。此外,正如 Cerberus 在问题评论中指出的那样,它会大大减慢编译器的速度。
换句话说:在 Rust 中,你永远不会显式移动某些东西。你想怎么弄就怎么弄,让编译器根据类型是不是Copy
,后面有没有用到,告诉你做错了没有。这与 C++ 不同,在 C++ 中,移动是一个可能调用 "move constructor" 并有副作用的操作;在 Rust 中,它是一个纯静态的 pass/fail 检查。如果你做对了,程序就会通过并进入下一阶段的编译;如果您做错了,借用检查器会告诉您(并希望能帮助您解决问题)。
另见
- Runtime vs Compile time
¹ 除非移动类型实现 Drop
,在这种情况下,编译器可能会发出 drop flags.
² 实际上,是(丢弃标志),但它只在 foo
被丢弃时检查,而不是在每次使用时检查。不实现 Drop
的类型没有丢弃标志,即使它们具有相同的移动语义。
³ 这类似于空检查在 Kotlin 中的工作方式:如果编译器可以确定引用肯定是非空的,它将允许您取消引用它。 Rust 中的有效性分析比这更保守;编译器甚至不会尝试猜测。