Отслеживание прав собственности в Rust: разница между Box<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"
}
Я подозреваю, что это как-то связано с природой стека и природой кучи, где первая является статической (по крайней мере, на кадр стека), а вторая - динамической. Возможно, компилятору нужно быть осторожным, потому что по какой-то причине я не могу сформулировать / определить достаточно хорошо.
1 ответ
В абстрактном struct
в стеке это просто набор переменных под общим именем. Компилятор знает это и может разбить структуру на набор независимых в другом отношении переменных стека. Это позволяет ему отслеживать движение каждого поля независимо.
Это не может сделать это с Box
или любой другой вид пользовательского размещения, потому что компилятор не контролирует Box
эс. Box
это просто некоторый код в стандартной библиотеке, а не внутренняя часть языка. Box
не может рассуждать о том, что разные его части внезапно становятся недействительными. Когда приходит время уничтожать Box
, его Drop
Реализация знает только уничтожить все.
Иными словами, в стеке компилятор находится под полным контролем и, таким образом, может делать такие причудливые вещи, как разбиение структур и перемещение их по частям. Как только пользовательское распределение входит в картину, все ставки отключены, и компилятор должен отступить и перестать пытаться быть умным.