Rust 中的所有权跟踪:盒子(<T>堆)和 T(堆栈)之间的区别



尝试使用编程语言 Rust,我发现编译器能够非常准确地跟踪堆栈上某个结构的字段的移动(它确切地知道哪个字段已经移动)。 但是,当我将结构的一部分放入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"
}

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

抽象地说,堆栈上的struct只是一堆通用名称下的变量。 编译器知道这一点,并且可以将结构分解为一组原本独立的堆栈变量。 这让它可以独立地跟踪每个字段的移动。

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

换句话说:在堆栈上,编译器完全控制,因此可以做一些花哨的事情,比如分解结构并将它们零碎地移动。 一旦自定义分配进入图片,所有的赌注都消失了,编译器必须退缩并停止试图变得聪明。

最新更新