Структура Rust
В Rust как структуры, так и кортежи могут объединять несколько фрагментов данных, возможно, разных типов в одно целое, но каждый член и сама структура имеют имя, поэтому при доступе к её членам не нужно запоминать индексы. Кортежи часто используются для ad-hoc-передачи нескольких значений, а структуры — для определения часто используемых структур данных. Каждый член структуры называется "полем".
Определение структуры
Это определение структуры:
struct Site {
domain: String,
name: String,
nation: String,
found: u32
}Примечание: если вы привыкли к C/C++, помните, что в Rust инструкция struct используется только для определения, она не может объявлять экземпляры, и ей не нужен;символ, а после определения каждого поля используйте,для разделения.
Экземпляр структуры
Rust во многих местах находится под влиянием JavaScript. При создании экземпляра структуры он использует синтаксисkey: valueобъекта JSON для определения:
Пример
domain: String::from("www.example.com"),
name: String::from("EXAMPLE"),
nation: String::from("China"),
found: 2013
};
Если вы не знаете объекты JSON, можете не обращать на них внимания и просто запомнить формат:
Struct class name {
field name : field value,
...
}Преимущество этого в том, что оно не только делает программу более интуитивной, но и не требует вводить значения полей в порядке их определения.
Если у создаваемой структуры имена полей совпадают с именами существующих переменных, можно упростить запись:
Пример
let domain = String::from("www.example.com");
let name = String::from("EXAMPLE");
let example = Site {
domain, // эквивалентно domain: domain,
name, // эквивалентно name: name,
nation: String::from("China"),
трафик: 2013
};
Есть ситуация: вы хотите создать новый экземпляр структуры, в котором большинство свойств должны быть установлены в те же значения, что и у существующей структуры, а изменить нужно только одно или два значения полей. Вы можете использовать синтаксис обновления структуры:
let site = Site {
domain: String::from("www.example.com"),
name: String::from("EXAMPLE"),
..example
};Примечание:..exampleПосле него не может быть запятой. Этот синтаксис не позволяет скопировать другой экземпляр структуры точно без изменений; то есть вы должны установить значение хотя бы одного поля, прежде чем ссылаться на значения другого экземпляра.
Кортежная структура
Есть более простой способ определять и использовать структуры:Кортежная структура。
Кортежная структура — это структура, имеющая форму кортежа.
Отличие от кортежа в том, что она имеет имя и фиксированный формат типов. Она существует для обработки простых данных, которым нужен определённый тип (для частого использования), не будучи слишком сложной:
struct Color(u8, u8, u8); struct Point(f64, f64); let black = Color(0, 0, 0); let origin = Point(0.0, 0.0);
"Color" и "координаты точки" — это два часто используемых типа данных, но если при создании экземпляра вы пишете фигурные скобки и два имени, вы жертвуете удобством ради читаемости. Rust не оставляет эту проблему нерешённой. Объекты кортежных структур используются так же, как кортежи, доступ осуществляется через . и индексы:
Пример
struct Color(u8, u8, u8);
struct Point(f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
println!("black = ({}, {}, {})", black.0, black.1, black.2);
println!("origin = ({}, {})", origin.0, origin.1);
}
Вывод:
black = (0, 0, 0) origin = (0, 0)
Владение структурой
Структура должна владеть значениями своих полей, потому что, когда структура выходит из области видимости, она освобождает все свои поля.
Вот почему в примерах этой главы используется тип String вместо &str.
Но это не означает, что поля-ссылки нельзя определять в структурах; для этого требуется механизм «времени жизни».
Но концепцию «времени жизни» сейчас трудно объяснить, поэтому её можно объяснить только в последующих главах.
Вывод структур
При отладке отображение полного экземпляра структуры очень полезно. Но вручную писать формат было бы очень неудобно. Поэтому Rust предоставляет удобный способ вывести всю структуру:
Пример
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
Как показано в первой строке: обязательно импортируйте библиотеку отладки#[derive(Debug)], затем в макросах println и print можно использовать{:?}заполнитель для вывода всей структуры:
rect1 is Rectangle { width: 30, height: 50 }
Если атрибутов много, можно использовать другой заполнитель{:#?} 。
Вывод:
rect1 is Rectangle {
width: 30,
height: 50
}
Методы структур
Методы похожи на функции, за исключением того, что они используются для работы с экземплярами структур.
Если вы изучали объектно-ориентированные языки, вы, должно быть, знаете, что функции обычно размещаются в определениях классов и используют "this" внутри функции для ссылки на экземпляр, с которым выполняется операция.
Rust — не объектно-ориентированный язык, что видно по инновации его механизма владения. Однако ценные идеи объектно-ориентированного программирования можно реализовать в Rust.
Первый параметр метода структуры должен быть &self, и его тип не нужно объявлять, потому что self — не стиль, а ключевое слово.
Вычислите площадь прямоугольника:
Пример
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("площадь rect1 равна {}", rect1.area());
}
Вывод:
rect1's area is 1500
Обратите внимание, что при вызове метода структуры не нужно указывать self; это сделано для удобства.
Пример с несколькими параметрами:
Пример
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn wider(&self, rect: &Rectangle) -> bool {
self.width > rect.width
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 40, height: 20 };
println!("{}", rect1.wider(&rect2));
}
Вывод:
false
Эта программа вычисляет, шире ли rect1, чем rect2.
Связанные функции структур
Причина, по которой "методы структуры" не называются "функциями структуры", заключается в том, что имя "функция" зарезервировано для функций, которые находятся в блоке impl, но не имеют параметра &self.
Такие функции не зависят от экземпляра, но чтобы их использовать, нужно указать, в каком блоке impl они находятся.
Всегда используемаяString::fromфункция — это "связанная функция".
Пример
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn create(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
}
fn main() {
let rect = Rectangle::create(30, 50);
println!("{:?}", rect);
}
Вывод:
Rectangle { width: 30, height: 50 }
Совет:Блок impl структуры можно записывать несколько раз; эффект эквивалентен объединению их содержимого!
Юнит-структура
Структура может служить символом без каких-либо членов:
struct UnitStruct;
Мы называем эту структуру без тела юнит-структурой.
Другие расширения