Rust编译器如何知道某个值是否已被移动?

时间:2016-01-14 08:18:47

标签: rust

一个简单的例子:

struct A;

fn main() {
    test(2);
    test(1);
}

fn test(i: i32) {
    println!("test");
    let a = A;
    if i == 2 {
        us(a);
    }
    println!("end");
}

impl Drop for A {
    fn drop(&mut self) {
        println!("drop");
    }
}

#[allow(unused_variables)]
fn us(a: A){
    println!("use");
}

当我运行它时,输出是:

test
use
drop
end
test
end
drop

我理解在test(2)案例中,a移动到us(a),因此它的输出是" test-use-drop-end"

但是,在test(1)中,输出是" test-end-drop",这意味着编译器知道a未被移动。

如果调用了us(a),则无需将a放入test(i),它将被删除us(a);如果未调用us(a),则必须在a之后删除println!("end")

由于编译器无法知道是否调用了us(a),编译器如何知道a.drop()之后是否应该调用println!("end")

1 个答案:

答案 0 :(得分:9)

Rustnomicon

中对此进行了解释
  

从Rust 1.0开始,drop flags实际上并没有秘密地隐藏在任何实现Drop的类型的隐藏字段中。

隐藏字段告诉当前值是否已被删除,如果没有,则表示当前值是否已被删除。因此,这在运行时是已知的,并且需要一些簿记。

展望未来,有一个到remove these hidden fields的RFC。

RFC的想法是用以下方法替换隐藏字段:

  1. 识别无条件丢弃(不需要任何运行时检查)
  2. 在功能框架中隐藏堆栈上的隐藏字段,以便有条件地删除这些值
  3. 这种新策略比旧策略有几个优点:

    • 主要优势在于,即使#[repr(C)]实现struct
    • Drop现在也会始终提供与C相同的表示形式
    • 另一个重要优势是节省内存(通过不膨胀struct大小)
    • 另一个小优势是由于无条件掉线和更好的缓存(减少内存大小)可能导致轻微的速度提升