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 есть возможность, которая даёт нам лучший способ реализовать это: обобщённое программирование. Обобщённое программирование называется «параметрическим полиморфизмом» в теории типов. Это означает, что типы или функции имеют несколько форм (poly — кратно, morph — форма) по данному параметру («параметрический»).

В любом случае, хватит о теории типов; давайте рассмотрим какой-нибудь обобщённый код. Стандартная библиотека Rust предоставляет тип Option<T>, который является обобщённым типом:

enum Option<T> {
    Some(T),
    None,
}Run

Часть <T>, которую вы раньше уже видели несколько раз, указывает, что это обобщённый тип данных. Внутри перечисления, везде, где мы видим T, мы подставляем вместо этого абстрактного типа тот, который используется в обобщении. Вот пример использования Option<T> с некоторыми дополнительными аннотациями типов:

let x: Option<i32> = Some(5);Run

В определении типа мы используем Option<i32>. Обратите внимание, что это очень похоже на Option<T>. С той лишь разницей, что, в данном конкретном Option, T имеет значение i32. В правой стороне выражения мы используем Some(T), где T равно 5. Так как 5 является представителем типа i32, то типы по обе стороны совпадают, поэтому компилятор счастлив. Если же они не совпадают, то мы получим ошибку:

let x: Option<f64> = Some(5);
// error: mismatched types: expected `core::option::Option<f64>`,
// found `core::option::Option<_>` (expected f64 but found integral variable)Run

Но это не значит, что мы не можем сделать Option<T>, который содержит f64! Просто типы должны совпадать:

let x: Option<i32> = Some(5);
let y: Option<f64> = Some(5.0f64);Run

Это просто прекрасно. Одно определение — многостороннее использование.

Обобщать можно более, чем по одному параметру. Рассмотрим другой обобщённый тип из стандартной библиотеки Rust — Result<T, E>:

enum Result<T, E> {
    Ok(T),
    Err(E),
}Run

Этот тип является обобщённым сразу для двух типов: T и E. Кстати, заглавные буквы могут быть любыми. Мы могли бы определить Result<T, E> как:

enum Result<A, Z> {
    Ok(A),
    Err(Z),
}Run

если бы захотели. Соглашение гласит, что первый обобщённый параметр для 'типа' должен быть T, и что для 'ошибки' используется E. Но Rust не проверяет этого.

Тип Result<T, E> предназначен для того, чтобы возвращать результат вычисления, и имеет возможность вернуть ошибку, если произойдёт какой-либо сбой.

Обобщённые функции

Мы можем задавать функции, которые принимают обобщённые типы, с помощью аналогичного синтаксиса:

fn takes_anything<T>(x: T) {
    // делаем что-то с x
}Run

Синтаксис состоит из двух частей: <T> говорит о том, что «эта функция является обобщённой по одному типу, T», а x: T говорит о том, что «х имеет тип T».

Несколько аргументов могут иметь один и тот же обобщённый тип:

fn takes_two_of_the_same_things<T>(x: T, y: T) {
    // ...
}Run

Мы можем написать версию, которая принимает несколько типов:

fn takes_two_things<T, U>(x: T, y: U) {
    // ...
}Run

Обобщённые функции наиболее полезны в связке с «ограничениями по типажам», о которых мы расскажем в главе Типажи.

Обобщённые структуры

Вы также можете задать обобщённый тип для struct:

struct Point<T> {
    x: T,
    y: T,
}

let int_origin = Point { x: 0, y: 0 };
let float_origin = Point { x: 0.0, y: 0.0 };Run

Аналогично функциям, мы также объявляем обобщённые параметры в <T>, а затем используем их в объявлении типа x: T.