1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Пользовательские менеджеры памяти

Выделение памяти — это не самая простая задача, и Rust обычно заботится об этом сам, но часто нужно тонко управлять выделением памяти. Компилятор и стандартная библиотека в настоящее время позволяют глобально переключить используемый менеджер во время компиляции. Описание сейчас находится в RFC 1183, но здесь мы рассмотрим как сделать ваш собственный менеджер.

Стандартный менеджер памяти

В настоящее время компилятор содержит два стандартных менеджера: alloc_system и alloc_jemalloc (однако у некоторых платформ отсутствует jemalloc). Эти менеджеры стандартны для контейнеров Rust и содержат реализацию подпрограмм для выделения и освобождения памяти. Стандартная библиотека не компилируется специально для использования только одного из них. Компилятор будет решать какой менеджер использовать во время компиляции в зависимости от типа производимых выходных артефактов.

По умолчанию исполняемые файлы сгенерированные компилятором будут использовать alloc_jemalloc (там где возможно). В таком случае компилятор "контролирует весь мир", в том смысле что у него есть власть над окончательной компоновкой.

Однако динамические и статические библиотеки по умолчанию будут использовать alloc_system. Здесь Rust обычно в роли гостя в другом приложении или вообще в другом мире, где он не может авторитетно решать какой менеджер использовать. В результате он возвращается назад к стандартным API (таких как malloc и free), для получения и освобождения памяти.

Переключение менеджеров памяти

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

#![feature(alloc_system)]

extern crate alloc_system;

fn main() {
    let a = Box::new(4); // выделение памяти с помощью системного менеджера
    println!("{}", a);
}Run

В этом примере сгенерированный исполняемый файл будет скомпонован с системным менеджером, вместо менеджера по умолчанию — jemalloc. И наоборот, чтобы сгенерировать динамическую библиотеку, которая использует jemalloc по умолчанию нужно написать:

#![feature(alloc_jemalloc)]
#![crate_type = "dylib"]

extern crate alloc_jemalloc;

pub fn foo() {
    let a = Box::new(4); // выделение памяти с помощью jemalloc
    println!("{}", a);
}Run

Написание своего менеджера памяти

Иногда даже выбора между jemalloc и системным менеджером недостаточно и необходим совершенно новый менеджер памяти. В этом случае мы напишем наш собственный контейнер, который будет предоставлять API менеджера памяти (также как и alloc_system или alloc_jemalloc). Для примера давайте рассмотрим упрощенную и аннотированную версию alloc_system:

// Компилятору нужно указать, что этот контейнер является менеджером памяти, для
// того что бы при компоновке он не использовал другой менеджер.
#![feature(allocator)]
#![allocator]

// Менеджерам памяти не позволяют зависеть от стандартной библиотеки, которая в
// свою очередь зависит от менеджера, чтобы избежать циклической зависимости.
// Однако этот контейнер может использовать все из libcore.
#![no_std]

// Давайте дадим какое-нибудь уникальное имя нашему менеджеру.
#![crate_name = "my_allocator"]
#![crate_type = "rlib"]

// Наш системный менеджер будет использовать поставляемый вместе с компилятором
// контейнер libc для связи с FFI. Имейте ввиду, что на данный момент внешний
// (crates.io) libc не может быть использован, поскольку он компонуется со
// стандартной библиотекой (`#![no_std]` все еще нестабилен).
#![feature(libc)]
extern crate libc;

// Ниже перечислены пять функций, необходимые пользовательскому менеджеру памяти.
// Их сигнатуры и имена на данный момент не проверяются компилятором, но это
// вскоре будет реализовано, так что они должны соответствовать тому, что
// находится ниже.
//
// Имейте ввиду, что стандартные `malloc` и `realloc` не предоставляют опций для
// выравнивания, так что эта реализация должна быть улучшена и поддерживать
// выравнивание.
#[no_mangle]
pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 {
    unsafe { libc::malloc(size as libc::size_t) as *mut u8 }
}

#[no_mangle]
pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) {
    unsafe { libc::free(ptr as *mut libc::c_void) }
}

#[no_mangle]
pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize,
                                _align: usize) -> *mut u8 {
    unsafe {
        libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
    }
}

#[no_mangle]
pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize,
                                        _size: usize, _align: usize) -> usize {
    old_size // libc не поддерживает этот API
}

#[no_mangle]
pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
    size
}
Run

После того как мы скомпилировали этот контейнер, мы можем использовать его следующим образом:

extern crate my_allocator;

fn main() {
    let a = Box::new(8); // выделение памяти с помощью нашего контейнера
    println!("{}", a);
}Run

Ограничения пользовательских менеджеров памяти

Несколько ограничений при работе с пользовательским менеджером памяти, которые могут быть причиной ошибок компиляции: