Давайте поговорим о некоторых пользовательских типах данных в 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
.
Счастливого программирования!!!