Структура Rust

В Rust как структуры, так и кортежи могут объединять несколько фрагментов данных, возможно, разных типов в одно целое, но каждый член и сама структура имеют имя, поэтому при доступе к её членам не нужно запоминать индексы. Кортежи часто используются для ad-hoc-передачи нескольких значений, а структуры — для определения часто используемых структур данных. Каждый член структуры называется "полем".

Определение структуры

Это определение структуры:

struct Site {
    domain: String,
    name: String,
    nation: String,
    found: u32
}

Примечание: если вы привыкли к C/C++, помните, что в Rust инструкция struct используется только для определения, она не может объявлять экземпляры, и ей не нужен;символ, а после определения каждого поля используйте,для разделения.

Экземпляр структуры

Rust во многих местах находится под влиянием JavaScript. При создании экземпляра структуры он использует синтаксисkey: valueобъекта JSON для определения:

Пример

let example = Site {
    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 не оставляет эту проблему нерешённой. Объекты кортежных структур используются так же, как кортежи, доступ осуществляется через . и индексы:

Пример

fn main() {
    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 предоставляет удобный способ вывести всю структуру:

Пример

#[derive(Debug)]

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 — не стиль, а ключевое слово.

Вычислите площадь прямоугольника:

Пример

struct Rectangle {
    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; это сделано для удобства.

Пример с несколькими параметрами:

Пример

struct Rectangle {
    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функция — это "связанная функция".

Пример

#[derive(Debug)]
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;

Мы называем эту структуру без тела юнит-структурой.

Другие расширения