Структуры
Структура — это другой вид агрегатного типа, как и кортеж. Разница в том, что в структурах у каждого элемента есть имя. Элемент структуры называется полем или членом структуры. Смотрите:
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
.