Отслеживание прав собственности в 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 Реализация знает только уничтожить все.

Иными словами, в стеке компилятор находится под полным контролем и, таким образом, может делать такие причудливые вещи, как разбиение структур и перемещение их по частям. Как только пользовательское распределение входит в картину, все ставки отключены, и компилятор должен отступить и перестать пытаться быть умным.

Другие вопросы по тегам