Куча и стек
Программы выделяют память двумя способами:
-
Стек: непрерывная область памяти для локальных переменных.
- Значения имеют фиксированный размер, известный во время компиляции.
- Чрезвычайно быстро: достаточно сдвинуть указатель стека.
- Легко управлять: следует за вызовами функций.
- Отличная локальность памяти.
-
Куча: хранилище значений вне вызовов функций.
- Значения имеют динамический размер, определяемый во время выполнения.
- Немного медленнее стека: требуется дополнительный учёт.
- Нет гарантии локальности памяти.
Пример
Создание String размещает метаданные фиксированного размера на стеке, а данные переменного размера, то есть саму строку, — в куче:
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
fn main() {
let s1 = String::from("Hello");
}
-
Отметьте, что
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}");
}
}