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. Библиография

Структуры

Структуры (struct) — это один из способов создания более сложных типов данных. Например, если мы рассчитываем что-то с использованием координат 2D пространства, то нам понадобятся оба значения — x и y:

let origin_x = 0;
let origin_y = 0;Run

Структура позволяет нам объединить эти два значения в один тип с x и y в качестве имен полей:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let origin = Point { x: 0, y: 0 }; // origin: Point

    println!("Начало координат находится в ({}, {})", origin.x, origin.y);
}Run

Этот код делает много разных вещей, поэтому давайте разберём его по порядку. Мы объявляем структуру с помощью ключевого слова struct, за которым следует имя объявляемой структуры. Обычно, имена типов-структур начинаются с заглавной буквы и используют чередующийся регистр букв: название PointInSpace выглядит привычно, а Point_In_Space — нет.

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

Наконец, поскольку у полей есть имена, мы можем получить к ним доступ с помощью операции точка: origin.x.

Значения, хранимые в структурах, неизменяемы по умолчанию. В этом плане они не отличаются от других именованных сущностей. Чтобы они стали изменяемы, используйте ключевое слово mut:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let mut point = Point { x: 0, y: 0 };

    point.x = 5;

    println!("Точка находится в ({}, {})", point.x, point.y);
}Run

Этот код напечатает Точка находится в (5, 0).

Rust не поддерживает изменяемость отдельных полей, поэтому вы не можете написать что-то вроде такого:

struct Point {
    mut x: i32,
    y: i32,
}Run

Изменяемость — это свойство имени, а не самой структуры. Если вы привыкли к управлению изменяемостью на уровне полей, сначала это может показаться непривычным, но на самом деле такое решение сильно упрощает вещи. Оно даже позволяет вам делать имена изменяемыми только на короткое время:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let mut point = Point { x: 0, y: 0 };

    point.x = 5;

    let point = point; // это новое имя неизменяемо

    point.y = 6; // это вызывает ошибку
}Run

Структуры так же могут содержать &mut ссылки, это позволяет вам производить подобные преобразования:

struct Point {
    x: i32,
    y: i32,
}

struct PointRef<'a> {
    x: &'a mut i32,
    y: &'a mut i32,
}

fn main() {
    let mut point = Point { x: 0, y: 0 };

    {
        let r = PointRef { x: &mut point.x, y: &mut point.y };

        *r.x = 5;
        *r.y = 6;
    }

    assert_eq!(5, point.x);
    assert_eq!(6, point.y);
}Run

Синтаксис обновления (update syntax)

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

struct Point3d {
    x: i32,
    y: i32,
    z: i32,
}

let mut point = Point3d { x: 0, y: 0, z: 0 };
point = Point3d { y: 1, .. point };Run

Этот код присваивает point новое y, но оставляет старые x и z. Это не обязательно должна быть та же самая структура — вы можете использовать этот синтаксис когда создаёте новые структуры, чтобы скопировать значения неуказанных полей:

let origin = Point3d { x: 0, y: 0, z: 0 };
let point = Point3d { z: 1, x: 2, .. origin };Run

Кортежные структуры

В Rust есть ещё один тип данных, который представляет собой нечто среднее между кортежем и структурой. Он называется кортежной структурой. Кортежные структуры именуются, а вот у их полей имён нет:

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);Run

Эти два объекта различны, несмотря на то, что у них одинаковые значения.

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

struct Color {
    red: i32,
    blue: i32,
    green: i32,
}

struct Point {
    x: i32,
    y: i32,
    z: i32,
}Run

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

Однако, есть один случай, когда кортежные структуры очень полезны. Это кортежная структура с всего одним элементом. Такое использование называется новым типом, потому что оно позволяет создать новый тип, отличный от типа значения, содержащегося в кортежной структуре. При этом новый тип обозначает что-то другое:

struct Inches(i32);

let length = Inches(10);

let Inches(integer_length) = length;
println!("Длина в дюймах: {}", integer_length);Run

Как вы можете видеть в данном примере, извлечь вложенный целый тип можно с помощью деконструирующего let. Мы обсуждали это выше, в разделе «кортежи». В данном случае, оператор let Inches(integer_length) присваивает 10 имени integer_length.

Unit-подобные структуры

Вы можете объявить структуру без полей вообще:

struct Electron;

let x = Electron;Run

Такие структуры называют «unit-подобные» («unit-like»), потому что они похожи на пустой кортеж (), иногда называемый «unit». Как и кортежные структуры, их называют новым типом.

Сами по себе они редко бывают полезны (хотя иногда их используют в качестве меток), но в сочетании с другими возможностями их использование имеет смысл. Например, для использования библиотеки может быть необходимо создать структуру, которая реализует определенный типаж для обработки событий. Если у вас нет данных, которые нужно поместить в структуру, то можно просто создать unit-подобную структуру.