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

时间:2019-12-07 03:20:40

标签: if-statement scope rust borrowing

对于以下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语句中,而这永远是不正确的。另外,我想进一步了解这种情况,应该使用哪些关键字搜索?

1 个答案:

答案 0 :(得分:5)

这是动作的秘密:它们并不真正存在。

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

那么编译器如何强制您在移动后不使用原始值?没有运行时标志可以跟踪foo是否有效。²而是,编译器使用源代码分析来确定在编译时foo是否绝对有效或是否已被移出。尝试使用它的时间。由于此分析是在编译时进行的,因此它不会遵循函数内的执行流程;它同时针对整个功能发生。编译器发现foo已移出if内部,并拒绝以后使用foo而不评估条件或任何代码。

智能编译器可以在进行有效性分析时将控制流纳入考虑范围,³但这可能不是一个改进。并非总是可能知道是否采用了分支(它是undecidable),因此在某些情况下编译器仍然会把它弄错。而且,正如Cerberus在问题注释中指出的那样,这将大大减慢编译器的通过速度。

采用另一种方式:在Rust中,您永远不会明确移动任何东西。您可以随便执行任何操作,然后让编译器根据类型是否为Copy以及以后是否使用它来告诉您是否做错了。这与C ++不同,在C ++中,移动是可以调用“移动构造函数”并具有副作用的操作。在Rust中,这是一个完全静态的通过/失败检查。如果操作正确,程序将继续进行并进入下一个编译阶段。如果您做错了,借用检查器会告诉您(并希望可以帮助您解决问题)。

另请参见


¹除非移动类型实现了Drop,否则编译器可能会发出drop flags

²实际上,存在 (放置标志),但是仅在放置foo时才检查它,而不是每次使用时都检查。未实现Drop的类型即使具有相同的移动语义也不会具有放置标志。

³这类似于Kotlin中的空检查工作原理:如果编译器可以确定引用绝对非空,则可以取消引用。 Rust中的有效性分析比这更保守。编译器甚至不会尝试猜测。