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 ей уделяется не меньше внимания, чем самому коду. Давайте поговорим об инструментах Rust, предназначенных для создания документации к проекту.

О rustdoc

Дистрибутив Rust включает в себя инструмент, rustdoc, который генерирует документацию. rustdoc также используется Cargo через cargo doc.

Документация может быть сгенерирована двумя методами: из исходного кода, и из отдельных файлов в формате Markdown.

Документирование исходного кода

Основной способ документирования проекта на Rust заключается в комментировании исходного кода. Для этой цели вы можете использовать документирующие комментарии:

/// Создаёт новый `Rc<T>`.
///
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
pub fn new(value: T) -> Rc<T> {
    // здесь реализация
}Run

Этот код генерирует документацию, которая выглядит так. В приведенном коде реализация метода была заменена на обычный комментарий. Первое, на что следует обратить внимание в этом примере, это на использование /// вместо //. Символы /// указывают, что это документирующий комментарий.

Документирующие комментарии пишутся на Markdown.

Rust отслеживает такие комментарии, и использует их при создании документации.

При документировании таких вещей, как перечисления, нужно учитывать некоторые особенности работы rustdoc. Такой код работает:

/// Тип `Option`. Подробнее смотрите [документацию уровня модуля](http://doc.rust-lang.org/).
enum Option<T> {
    /// Нет значения
    None,
    /// Некоторое значение `T`
    Some(T),
}Run

А такой — нет:

/// Тип `Option`. Подробнее смотрите [документацию уровня модуля](http://doc.rust-lang.org/).
enum Option<T> {
    None, /// Нет значения
    Some(T), /// Некоторое значение `T`
}Run

Вы получите ошибку:

hello.rs:4:1: 4:2 error: expected ident, found `}`
hello.rs:4 }
           ^

Эта досадная ошибка заключается в следующем: комментарии документации распространяются на элементы, расположенные за ними, а в данном примере нет элемента, расположенного после последнего комментария.

Написание комментариев документации

Давайте рассмотрим каждую часть приведенного комментария в деталях:

/// Создаёт новый `Rc<T>`.Run

Первая строка документирующего комментария должна представлять из себя краткую информацию о функциональности. Одно предложение. Только самое основное. Высокоуровневое.

///
/// Подробности создания `Rc<T>`, возможно, описывающие сложности семантики,
/// дополнительные опции, и всё остальное.
///Run

Наш исходный пример включал только строку с краткой информацией, но если бы у нас было больше информации, о которой следует сказать, мы могли бы добавить эту информацию в новом параграфе.

Специальные разделы

/// # ExamplesRun

Далее идут специальные разделы. Они обознаются заголовком, который начинается с #. Существуют три вида заголовков, которые обычно используются. Они не являются каким-либо специальным синтаксисом, на данный момент это просто соглашение.

/// # PanicsRun

Раздел Panics. Неустранимые ошибки при неправильном вызове функции (так называемые ошибки программирования) в Rust, как правило, вызывают панику, которая, в крайнем случае, убивает весь текущий поток (thread). Если ваша функция имеет подобное нетривиальное поведение — т.е. обнаруживает/вызывает панику, то очень важно задокументировать это.

/// # FailuresRun

Раздел Failures. Если ваша функция или метод возвращает Result<T, E>, то хорошим тоном является описание условий, при которых она возвращает Err(E). Это чуть менее важно, чем описание Panics, потому как неудача кодируется в системе типов, но это не значит, что стоит пренебрегать данной возможностью.

/// # SafetyRun

Раздел Safety. Если ваша функция является unsafe, необходимо пояснить, какие инварианты вызова должны поддерживаться.

/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```Run

Раздел Examples. Включите в этот раздел один или несколько примеров использования функции или метода, и ваши пользователи будут вам благодарны. Примеры должны размещаться внутри блоков кода, о которых мы сейчас поговорим. Этот раздел может иметь более одного подраздела:

/// # Examples
///
/// Простые образцы типа `&str`:
///
/// ```
/// let v: Vec<&str> = "И была у них курочка Ряба".split(' ').collect();
/// assert_eq!(v, vec!["И", "была", "у", "них", "курочка", "Ряба"]);
/// ```
///
/// Более сложные образцы с замыканиями:
///
/// ```
/// let v: Vec<&str> = "абв1где2жзи".split(|c: char| c.is_numeric()).collect();
/// assert_eq!(v, vec!["абв", "где", "жзи"]);
/// ```Run

Давайте подробно обсудим блоки кода.

Блок кода

Чтобы написать код на Rust в комментарии, используйте символы ```:

/// ```
/// println!("Привет, мир");
/// ```Run

Если вы хотите написать код на любом другом языке (не на Rust), вы можете добавить аннотацию:

/// ```c
/// printf("Hello, world\n");
/// ```Run

Это позволит использовать подсветку синтаксиса, соответствующую тому языку, который был указан в аннотации. Если же это простой текст, то в аннотации указывается text.

Важно выбрать правильную аннотацию, потому что rustdoc использует ее интересным способом: Rust может выполнять проверку работоспособности примеров на момент создания документации. Это позволяет избежать устаревания примеров. Предположим, у вас есть код на C. Если вы опустите аннотацию, указывающую, что это код на C, то rustdoc будет думать, что это код на Rust, поэтому он пожалуется при попытке создания документации.

Тесты в документации

Давайте обсудим наш пример документации:

/// ```
/// println!("Привет, мир");
/// ```Run

Заметьте, что здесь нет нужды в fn main() или чём-нибудь подобном. rustdoc автоматически добавит оборачивающий main() вокруг вашего кода в нужном месте. Например:

/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```Run

В конечном итоге это будет тест:

fn main() {
    use std::rc::Rc;
    let five = Rc::new(5);
}Run

Вот полный алгоритм, который rustdoc использует для обработки примеров:

  1. Любые ведущие (leading) атрибуты #![foo] остаются без изменений в качестве атрибутов контейнера.
  2. Будут вставлены некоторые общие атрибуты allow, в том числе: unused_variables, unused_assignments, unused_mut, unused_attributes, dead_code. Небольшие примеры часто приводят к срабатыванию этих анализов.
  3. Если пример не содержит extern crate, то будет вставлено extern crate <mycrate>;.
  4. Наконец, если пример не содержит fn main, то оставшаяся часть текста будет обернута в fn main() { your_code }

Хотя иногда этого не достаточно. Например, что насчёт всех этих примеров кода с ///, о которых мы говорили? Простой текст, обработанный rustdoc, выглядит так:

/// Некоторая документация.
# fn foo() {}

А исходный текст на Rust после обработки выглядит так:

/// Некоторая документация.Run

Да, именно так: вы можете добавлять строки, которые начинаются с #, и они будут скрыты в выводе, но при этом будут использоваться во время компиляции кода. Вы можете использовать это в своих интересах. Если в документирующем комментарии необходимо обратиться к какой-то функции, то ниже нужно будет добавить определение этой функции. В то же время, это делается только для того, чтобы удовлетворить компилятор, поэтому сокрытие ненужных строк в выводе делает пример более ясным. Вы можете использовать эту технику, чтобы детально объяснять длинные примеры, сохраняя при этом тестируемость документации. Например, вот код:

let x = 5;
let y = 6;
println!("{}", x + y);Run

Ниже приведено отрисованное объяснение этого кода.

Сперва мы устанавливаем x равным пяти:

let x = 5;Run

Затем мы устанавливаем y равным шести:

let y = 6;Run

В конце мы печатаем сумму x и y:

println!("{}", x + y);Run

А вот то же самое объяснение, но в виде простого текста:

Сперва мы устанавливаем x равным пяти:

let x = 5;
# let y = 6;
# println!("{}", x + y);

Затем мы устанавливаем y равным шести:

# let x = 5;
let y = 6;
# println!("{}", x + y);

В конце мы печатаем сумму x и y:

# let x = 5;
# let y = 6;
println!("{}", x + y);

Повторяя все части примера, вы можете быть уверены, что ваш пример компилируется, а не просто отображает кусочки кода, которые как-то относятся к той или иной части вашего объяснения.

Документирование макросов

Вот пример документирования макроса:

/// Паниковать с данным сообщением, если только выражение не является истиной.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(1 + 1 == 2, "Математика сломалась.");
/// # }
/// ```
///
/// ```should_panic
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(true == false, "Я сломан.");
/// # }
/// ```
#[macro_export]
macro_rules! panic_unless {
    ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } });
}Run

В нем вы можете заметить три вещи. Во-первых, мы должны собственноручно добавить строку с extern crate для того, чтобы мы могли указать атрибут #[macro_use]. Во-вторых, мы также собственноручно должны добавить main(). И наконец, разумно будет использовать #, чтобы закомментировать все, что мы добавили в первых двух пунктах, что бы оно не отображалось в генерируемом выводе.

Запуск тестов в документации

Для запуска тестов можно использовать одну из двух комманд

$ rustdoc --test path/to/my/crate/root.rs
# или
$ cargo test

Все верно, cargo test также выполняет тесты, встроенные в документацию. Тем не менее, cargo test не будет тестировать исполняемые контейнеры, только библиотечные. Это связано с тем, как работает rustdoc: он компонуется с библиотекой, которую надо протестировать, но в случае с исполняемым файлом компоноваться не с чем.

Есть еще несколько полезных аннотаций, которые помогают rustdoc работать правильно при тестировании кода:

/// ```ignore
/// fn foo() {
/// ```Run

Аннотация ignore указывает Rust, что код должен быть проигнорирован. Почти во всех случаях это не то, что вам нужно, так как эта директива носит очень общий характер. Вместо неё лучше использовать аннотацию text, если это не код, или #, чтобы получить рабочий пример, отображающий только ту часть, которая вам нужна.

/// ```should_panic
/// assert!(false);
/// ```Run

Аннотация should_panic указывает rustdoc, что код должен компилироваться, но выполнение теста должно завершиться ошибкой.

/// ```no_run
/// loop {
///     println!("Привет, мир");
/// }
/// ```Run

Аннотация no_run указывает, что код должен компилироваться, но запускать его на выполнение не требуется. Это важно для таких примеров, которые должны успешно компилироваться, но выполнение которых оказывается бесконечным циклом! Например: «Вот как запустить сетевой сервис».

Документирование модулей

Rust предоставляет ещё один вид документирующих комментариев, //!. Этот комментарий относится не к следующему за ним элементу, а к элементу, который его включает. Другими словами:

mod foo {
    //! Это документация для модуля `foo`.
    //!
    //! # Examples

    // ...
}Run

Приведённый пример демонстрирует наиболее распространённое использование //!: документирование модуля. Если же модуль расположен в файле foo.rs, то вы, открывая его код, часто будете видеть следующее:

//! Модуль использования разных `foo`.
//!
//! Модуль `foo` содержит много полезной функциональности ла-ла-лаRun

Стиль документирующих комментариев

Изучите RFC 505 для получения полных сведений о соглашениях по стилю и формату документации.

Другая документация

Все эти правила поведения также применимы и в отношении исходных файлов не на Rust. Так как комментарии пишутся на Markdown, то часто эти файлы имеют расширение .md.

Когда вы пишете документацию в файлах Markdown, вам не нужно добавлять префикс документирующего комментария, ///. Например:

/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```Run

преобразуется в

# Examples

```
use std::rc::Rc;

let five = Rc::new(5);
```

когда он находится в файле Markdown. Однако есть один недостаток: файлы Markdown должны иметь заголовок наподобие этого:

% Заголовок

Это пример документации.

Строка, начинающаяся с %, должна быть самой первой строкой файла.

Атрибуты doc

На более глубоком уровне, комментарии документации — это синтаксический сахар для атрибутов документации:

/// this

#[doc="this"]Run

Т.е. представленные выше комментарии идентичны, также как и ниже:

//! this

#![doc="/// this"]Run

Вы не часто будете видеть этот атрибут, используемый для написания документации, но он может быть полезен для изменения некоторых настроек, или при написании макроса.

Ре-экспорт

rustdoc будет показывать документацию для общедоступного (public) ре-экспорта в двух местах:

extern crate foo;

pub use foo::bar;Run

Это создаст документацию для bar как в документации для контейнера foo, так и в документации к вашему контейнеру. То есть в обоих местах будет использована одна и та же документация.

Такое поведение может быть подавлено с помощью no_inline:

extern crate foo;

#[doc(no_inline)]
pub use foo::bar;Run

Управление HTML

Вы можете управлять некоторыми аспектами HTML, который генерирует rustdoc, через атрибут #![doc]:

#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/")];Run

В этом примере устанавливается несколько различных опций: логотип, иконка и корневой URL.

Опции генерации

rustdoc также содержит несколько опций командной строки для дальнейшей настройки:

Замечание по безопасности

Комментарии в документации в формате Markdown помещаются в конечную веб-страницу без обработки. Будьте осторожны с HTML-литералами:

/// <script>alert(document.cookie)</script>Run