Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Куча и стек

Программы выделяют память двумя способами:

  • Стек: непрерывная область памяти для локальных переменных.

    • Значения имеют фиксированный размер, известный во время компиляции.
    • Чрезвычайно быстро: достаточно сдвинуть указатель стека.
    • Легко управлять: следует за вызовами функций.
    • Отличная локальность памяти.
  • Куча: хранилище значений вне вызовов функций.

    • Значения имеют динамический размер, определяемый во время выполнения.
    • Немного медленнее стека: требуется дополнительный учёт.
    • Нет гарантии локальности памяти.

Пример

Создание String размещает метаданные фиксированного размера на стеке, а данные переменного размера, то есть саму строку, — в куче:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let s1 = String::from("Hello");
}
StackHeaps1capacity5ptrHellolen5
This slide should take about 5 minutes.
  • Отметьте, что String основан на Vec, поэтому у него есть ёмкость и длина, и он может расти (если изменяем) за счёт перевыделения памяти в куче.

  • Если студенты спросят, можно упомянуть, что базовая память выделяется в куче через системный аллокатор, а собственные аллокаторы можно реализовать через Allocator API.

Для дальнейшего изучения

Можно исследовать раскладку памяти с помощью unsafe Rust. Однако стоит подчеркнуть, что это действительно небезопасно!

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let mut s1 = String::from("Hello");
    s1.push(' ');
    s1.push_str("world");
    // НЕ ДЕЛАЙТЕ ТАК ДОМА! Только в учебных целях.
    // String не даёт никаких гарантий о своей раскладке, поэтому это может привести к
    // неопределённому поведению.
    unsafe {
        let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
        println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
    }
}