Структуры
Структура — это другой вид агрегатного типа, как и кортеж. Разница в том, что в структурах у каждого элемента есть имя. Элемент структуры называется полем или членом структуры. Смотрите:
struct Point {
    x: i32,
    y: i32,
}
fn main() {
    let origin = Point { x: 0, y: 0 }; // origin: Point
    println!("Начало координат находится в ({}, {})", origin.x, origin.y);
}
Этот код делает много разных вещей, поэтому давайте разберём его по порядку. Мы
объявляем структуру с помощью ключевого слова 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);
}
Этот код напечатает Точка находится в (5, 0).
Rust не поддерживает изменяемость отдельных полей, поэтому вы не можете написать что-то вроде такого:
struct Point {
    mut x: i32,
    y: i32,
}
Изменяемость — это свойство имени, а не самой структуры. Если вы привыкли к управлению изменяемостью на уровне полей, сначала это может показаться непривычным, но на самом деле такое решение сильно упрощает вещи. Оно даже позволяет вам делать имена изменяемыми только на короткое время:
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; // это вызывает ошибку
}
Синтаксис обновления (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 };
Этот код присваивает point новое y, но оставляет старые x и z. Это не
обязательно должна быть та же самая структура — вы можете использовать этот
синтаксис когда создаёте новые структуры, чтобы скопировать значения неуказанных
полей:
# struct Point3d {
#     x: i32,
#     y: i32,
#     z: i32,
# }
let origin = Point3d { x: 0, y: 0, z: 0 };
let point = Point3d { z: 1, x: 2, .. origin };
Кортежные структуры
В Rust есть ещё один тип данных, который представляет собой нечто среднее между кортежем и структурой. Он называется кортежной структурой. Кортежные структуры именуются, а вот у их полей имён нет:
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
Эти два объекта различны, несмотря на то, что у них одинаковые значения:
# struct Color(i32, i32, i32);
# struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
Почти всегда, вместо кортежной структуры лучше использовать обычную структуру.
Мы бы скорее объявили типы Color и Point вот так:
struct Color {
    red: i32,
    blue: i32,
    green: i32,
}
struct Point {
    x: i32,
    y: i32,
    z: i32,
}
Теперь у нас есть настоящие имена, а не только позиции. Хорошие имена важны, и при использовании структуры у нас есть эти имена.
Однако, есть один случай, когда кортежные структуры очень полезны. Это кортежная структура с всего одним элементом. Такое использование называется новым типом, потому что оно позволяет создать новый тип, отличный от типа значения, содержащегося в кортежной структуре. При этом новый тип обозначает что-то другое:
struct Inches(i32);
let length = Inches(10);
let Inches(integer_length) = length;
println!("Длина в дюймах: {}", integer_length);
Как вы можете видеть в данном примере, извлечь вложенный целый тип можно с
помощью деконструирующего let. Мы обсуждали это выше, в разделе «кортежи». В
данном случае, оператор let Inches(integer_length) присваивает 10 имени
integer_length.