Rust — Struct, Generics

Давайте поговорим о некоторых пользовательских типах данных в Rust, таких как struct и emuns. Все мы знаем, зачем нужен пользовательский тип данных, как и в любом другом языке, обычный тип данных может не подойти, и поэтому у нас есть пользовательские типы данных.

Структуры, как их использовать в Rust?

Структура, struct сокращенно, очень похожа на tuple. Кортеж используется для хранения связанных элементов со смешанным типом данных в Order. Если в примере используется большое количество элементов, а порядок не является очевидным, то может быть трудно определить их из кортежей.

the first element of tuple [a_tuple] is 1
the last element of tuple [a_tuple] is Rust
the first element of tuple [a_tuple] after modification is 6
the last element of tuple [a_tuple] after modification is Rustic
Вход в полноэкранный режим Выйти из полноэкранного режима

Итак, вы видите, что если количество элементов больше, чем обычно, то становится очень трудно отслеживать порядок, и тогда мы используем struct.

Данные Struct обычно хранятся в стеке, поскольку они содержат только стековые типы данных, например, числа. Для хранения Struct в куче вы специально упомянули об этом. Также, если ваш Struct содержит типы данных кучи, такие как String, то он будет храниться в куче, а ссылки, ассоциированные данные данных кучи будут храниться в стеке. Поэтому, когда ваш экземпляр Struct выйдет из области видимости, связанные с ним данные в куче будут автоматически сброшены.

Определение struct в Rust похоже на Golang. Golang требует ключевое слово type наряду с ключевым словом struct.

Структура struct похожа на tuple, которая позволяет вам объединять связанные элементы со смешанным типом данных, однако вам не нужно использовать индекс для доступа к элементам, вместо этого вы используете имя поля для доступа к ним.

Структура также позволяет нам обновлять instance из другого instance, а синтаксис довольно прост и называется синтаксис обновления, который в основном говорит complier, что если в экземпляре отсутствует поле, то оно должно содержать то же поле из предыдущего экземпляра.

let second_car = Car {
  name : String::*from*("Tesla"),
  model : String::from("Model 3"),
  ..new_car,
};
Вход в полноэкранный режим Выход из полноэкранного режима

Любое обновление первого экземпляра после инициализации второго экземпляра не будет отражено во втором экземпляре.

Если вы видите, что недостающие поля имеют тип данных int, то это означает, что они находятся в стеке, поэтому они неявно копируются. Однако, если бы был задействован какой-либо тип данных String, это привело бы к ошибке, так как это нарушает правило владения rust. Чтобы это работало, нам нужно использовать явный клон для копирования данных из первого экземпляра, что-то вроде этого ..new_car.clone(). Чтобы узнать больше о владении и заимствовании. Также нам нужно добавить trait, поскольку тип данных Car не имеет trait для Clone() данных, поэтому нам нужно вывести это в определении структуры, что-то вроде этого #[derive(Clone)].

У структуры есть методы

В Rust мы можем вызывать подпрограммы, которые являются методами для структуры. Они похожи на функции и определяются с помощью ключевого слова fn. Разница между методами и функциями заключается в том, что методы всегда находятся в контексте struct и первым входным параметром всегда является сама struct.

Для определения метода нам нужно ключевое слово impl (сокращение от implementation), за которым следует имя struct.

Структура также имеет функцию

Rust также позволяет нам создавать ассоциированные функции, они очень похожи на method, однако не принимают &self в качестве аргумента. В основном они используются для инициализации нового экземпляра пользовательского типа данных, подобно конструкторам в других объектно-ориентированных языках.

Существует так называемый Struct Tuple, который представляет собой комбинацию Struct и Tuple. В Rust Struct Tuple’ы определяются так же, как и Struct, но у них нет именованного поля. Обычно это делается для создания пользовательского типа со смешанными первичными типами данных, но без необходимости именовать поля. Что-то вроде struct CarFeatures(4, "electric", "falcon doors").

Вот как выглядит весь код

Car Name: Runner, Model: Tesla Model Y, Year: 2022, Price: $70000/-
Price of Tesla Model Y has increased to $75000/-
Car Name: Beast, Model: Tesla Plaid, Year: 2022, Price: $110000/-
Price of Tesla Plaid has increased to $135000/-
Вход в полноэкранный режим Выйти из полноэкранного режима

Общие типы … Ура!!!

Rust является языком статических типов, поэтому определенный struct, function или method может быть использован только для своих определенных переменных типов данных. Это означает, что вы можете сохранить одно и то же тело кода для struct, function или method с разными типами данных. Что если мы сможем определить struct, function или method таким образом, чтобы использовать с ними любой тип данных. Enters…..Generic Type!!!

Выше мы имеем struct Car с общим типом (обозначаемым с помощью <...>) <S,I,T>, который передает type для полей struct. Аналогично, блок impl для определения методов new_car и update_price. Они также используют общие типы. И наконец, функция choose с generics, используемая для получения подтверждения правильности выбора на основе ценовых ограничений electric_per_unit и gas_per_gallon.

Пока не беспокойтесь о traits, таких как std::cmp::PartialOrd и std::ops::AddAssign. У меня будет отдельный блог с их объяснением. Пока они нам нужны, потому что rust компилятор не знает, какие типы данных будут у generics для выполнения comparison и addition.

Тип данных ящика

Еще один момент, которого я хочу коснуться, это тип данных Box в rust. Box datatype обычно используется для размещения данных в куче вместо стека. Проще говоря, стек обычно имеет небольшой размер, и когда вы храните данные, которые могут быть большого размера, например, trait или struct (комбинация различных типов данных и размеров), вы можете захотеть хранить их в куче, а ссылки на них хранить в стеке.

Примечание: Если вы делаете данные коробочными, это означает перемещение их из стека в кучу с помощью box datatype. При этом выполняется операция перемещения, а не копирования, поэтому предыдущее место в стеке будет деаллоцировано.

I bought Model X in 2021 for $120000, it is an electric Vehicle
Market prediction is that Model X in 2022 will be for $140000
BEFORE BOXING, Car struct data size is 56 bytes in Stack
Price Changed: in Inventory aka Stack, Now Price is $140000
BOXING the data.....
AFTER BOXING, Car struct data size is 8 bytes in Stack
Car struct data size is 56 bytes in Heap, because we are using de-referencing operator `*` to access the data
DATA AFTER BOXING: Model X Price: $140000 Fuel Type: electric
Вход в полноэкранный режим Выход из полноэкранного режима

Как видите, когда я использовал переменную boxed_car как тип box, данные были перемещены из стека в кучу. Теперь стек содержит только ссылку на данные (следовательно, 8 байт), а фактические данные находятся в куче (следовательно, 56 байт).
и мы используем & для ссылки на данные в стеке и оператор отмены ссылки * для получения размера данных в куче.

Надеюсь, это объясняет некоторые внутренние особенности и использование structs и generics в Rust. Я буду писать больше о других важных концепциях Rust.

Счастливого программирования!!!

Оцените статью
devanswers.ru
Добавить комментарий