5

使用编程语言 Rust 进行实验,我发现编译器能够非常准确地跟踪堆栈上某个结构的字段的移动(它确切地知道移动了哪个字段)。但是,当我将结构的一部分放入 a Box(即,将其放入堆中)时,编译器不再能够确定在取消引用框后发生的所有事情的字段级移动。它将假设“盒子内”的整个结构已经移动。让我们首先看一个所有内容都在堆栈中的示例:

struct OuterContainer {
    inner: InnerContainer
}

struct InnerContainer {
    val_a: ValContainer,
    val_b: ValContainer
}

struct ValContainer {
    i: i32
}


fn main() {
    // Note that the whole structure lives on the stack.
    let structure = OuterContainer {
        inner: InnerContainer {
            val_a: ValContainer { i: 42 },
            val_b: ValContainer { i: 100 }
        }
    };

    // Move just one field (val_a) of the inner container.
    let move_me = structure.inner.val_a;

    // We can still borrow the other field (val_b).
    let borrow_me = &structure.inner.val_b;
}

现在是相同的示例,但有一个细微的变化:我们将InnerContainer放入一个盒子 ( Box<InnerContainer>)。

struct OuterContainer {
    inner: Box<InnerContainer>
}

struct InnerContainer {
    val_a: ValContainer,
    val_b: ValContainer
}

struct ValContainer {
    i: i32
}


fn main() {
    // Note that the whole structure lives on the stack.
    let structure = OuterContainer {
        inner: Box::new(InnerContainer {
            val_a: ValContainer { i: 42 },
            val_b: ValContainer { i: 100 }
        })
    };

    // Move just one field (val_a) of the inner container.
    // Note that now, the inner container lives on the heap.
    let move_me = structure.inner.val_a;

    // We can no longer borrow the other field (val_b).
    let borrow_me = &structure.inner.val_b; // error: "value used after move"
}

我怀疑它与堆栈的性质与堆的性质有关,前者是静态的(至少每个堆栈帧),而后者是动态的。也许编译器需要安全地发挥它,因为某种原因我不能很好地表达/识别。

4

1 回答 1

13

在抽象中,struct堆栈上的a只是一堆具有共同名称的变量。编译器知道这一点,并且可以将结构分解为一组其他独立的堆栈变量。这使它可以独立跟踪每个字段的移动。

它不能用 aBox或任何其他类型的自定义分配来做到这一点,因为编译器不控制Boxes。 Box只是标准库中的一些代码,而不是语言的固有部分。 Box无法推理自身的不同部分突然变得无效。当需要销毁 aBox时,它的Drop实现只知道销毁一切

换句话说:在堆栈上,编译器处于完全控制之中,因此可以做一些花哨的事情,比如分解结构并逐个移动它们。一旦自定义分配进入画面,所有的赌注都被取消了,编译器不得不退后并停止试图变得聪明。

于 2017-05-20T04:34:28.953 回答