"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 中的有效性分析比这更保守;编译器甚至不会尝试猜测。