在 Rust 程序中,当条件为“假”时执行“if”语句,如何理解?

Jus*_*ner 1 scope if-statement rust borrowing

对于以下 Rust 程序:

fn main() {
    let foo = "test".to_string();
    if false {
        let _bar = foo; // value moved to _bar
    }
    println!("{}", foo);
}
Run Code Online (Sandbox Code Playgroud)

运行时出现这个错误:

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
Run Code Online (Sandbox Code Playgroud)

谁能帮忙解释一下这里发生了什么?令我感到奇怪的是,这一举动发生在永远不会为真的 if 语句中。另外我想了解更多关于这种情况,我应该使用哪些关键字来搜索?

tre*_*tcl 5

这是移动的秘诀:它们实际上并不存在。

移动不会生成与按位复制不同的代码(就机器代码而言)。¹ 移动和复制之间的唯一区别在于“原始”发生了什么:如果它仍然有效,则它是一个副本;如果原件不再有效,那就是一个举动。

那么编译器如何强制您在移动后不使用原始值呢?没有运行时标志来跟踪是否foo有效。² 相反,编译器使用源代码分析在编译时确定是否foo绝对有效或在您尝试使用它时是否可能已被移出。因为这种分析发生在编译时,它不遵循函数内的执行流程;它立即发生在整个函数中。编译器看到foo被移出 内部if,并拒绝稍后使用foo而不评估条件或任何代码。

智能编译器可以在进行有效性分析时考虑控制流,³ 但这可能不是改进。并不总是可以知道是否采用了分支(它是不可判定的),因此在某些情况下编译器仍然会出错。此外,正如 Cerberus 在问题评论中指出的那样,它会大大减慢编译器的速度。

换句话说:在 Rust 中,你永远不会明确移动某些东西。你用它做任何你想做的事,让编译器告诉你你是否做错了,根据类型Copy是否是以及以后是否使用它。这与 C++ 不同,在 C++ 中,移动是一种可能调用“移动构造函数”并具有副作用的操作;在 Rust 中,它是一个纯静态的通过/失败检查。如果你做对了,程序就会通过并进入下一个编译阶段;如果你做错了,借阅检查员会告诉你(并希望能帮助你解决它)。

也可以看看


¹ 除非被移动的类型实现Drop,在这种情况下编译器可能会发出丢弃标志

²其实,有(下降标志),但如果它只是检查foo被丢弃,而不是在每次使用。未实现的类型Drop没有丢弃标志,即使它们具有相同的移动语义。

³ 这类似于 Kotlin 中空检查的工作方式:如果编译器可以确定一个引用绝对是非空的,它将允许您取消引用它。Rust 中的有效性分析比这更保守;编译器甚至不会尝试猜测。