- Шпаргалка по языку Rust
- Введение
- Переменные
- Типы данных
- Работа с типами данных
- Арифметические операции в Rust
- Поразрядные операции в Rust
- Условные выражения
- Match как альтернатива if
- Циклы
- Функции
- Замыкания
- Параметры функций
- Возвращаемые значения
- Константы
- Тип функции
- Функции как параметры и возвращаемые значения
- Кортежи
- Массивы
- Структуры
- Структуры-кортежи
- Перечисления
- Последовательности
- Сопоставление с образом
Программа на Rust обычно имеет следующую структуру:
Функция main
Каждая программа на Rust должна содержать функцию main()
, которая является точкой входа в программу:
fn main() {
// Код программы
}
Инструкции
Тело функции main()
состоит из последовательности инструкций, каждая из которых завершается точкой с запятой:
fn main() {
println!("Hello Rust!");
let x = 5;
// Другие инструкции
}
Блоки кода
Блоки кода заключаются в фигурные скобки и могут содержать несколько инструкций:
fn main() {
{
let x = 10;
println!("x = {}", x);
}
}
Модули
Для организации кода используются модули. Модули могут быть определены в одном файле или разделены на несколько файлов:
mod something {
pub struct A {
pub a: i32,
}
}
use crate::something::*;
fn main() {
let first = A { a: 42 };
}
Файловая структура
Типичная структура проекта на Rust:
src/
|_ main.rs
|_ something/
|_ mod.rs
|_ a.rs
|_ b.rs
- Комментарии: Однострочные (
//
) и многострочные (/* */
) - Макросы: Например,
println!()
для вывода на консоль - Импорт: Использование
use
для импорта модулей и типов - Объявление переменных: С помощью ключевого слова
let
- Циклы:
loop
,while
,for
для повторения кода - Условные конструкции:
if
,else
,match
для ветвления кода
Эта базовая структура позволяет организовать код Rust в логические блоки и модули, обеспечивая читаемость и поддерживаемость программы.
Переменные в Rust имеют несколько ключевых особенностей:
Переменные в Rust объявляются с помощью ключевого слова let
:
let x = 5;
При объявлении можно явно указать тип переменной:
let y: i32 = 10;
В Rust переменные по умолчанию являются неизменяемыми (иммутабельными). Это означает, что после присвоения значения его нельзя изменить. Для создания изменяемой переменной используется ключевое слово mut
:
let mut z = 15;
z = 20; // Теперь это допустимо
В Rust можно объявить новую переменную с тем же именем, что и у существующей. Это называется затенением (shadowing):
let a = 5;
let a = a + 1; // a теперь равно 6
Rust - статически типизированный язык. Основные типы данных включают:
- Целочисленные:
i8
,i16
,i32
,i64
,u8
,u16
,u32
,u64
- С плавающей точкой:
f32
,f64
- Логический:
bool
- Символьный:
char
- Строковый:
&str
,String
Компилятор Rust часто может самостоятельно вывести тип переменной, если он не указан явно.
Переменные в Rust имеют блочную область видимости. Они существуют только в пределах блока, в котором объявлены.
Rust предупреждает о неиспользуемых переменных. Чтобы избежать предупреждения, можно добавить подчеркивание перед именем переменной:
let _unused = 5;
Эти особенности делают работу с переменными в Rust безопасной и предсказуемой, что помогает избежать многих распространенных ошибок программирования.
В Rust существует несколько основных категорий типов данных:
Целочисленные типы
- Со знаком: i8, i16, i32, i64, i128, isize
- Без знака: u8, u16, u32, u64, u128, usize
Например:
let x: i32 = 42;
let y: u64 = 100;
Числа с плавающей точкой
- f32 (32-битное)
- f64 (64-битное, используется по умолчанию)
let pi: f64 = 3.14159;
Логический тип
- bool (true или false)
let is_rust_cool: bool = true;
Символьный тип
- char (Unicode-символ)
let letter: char = 'A';
Кортежи
Группируют разные типы данных фиксированной длины:
let tup: (i32, f64, char) = (500, 6.4, 'A');
Массивы
Фиксированной длины, элементы одного типа:
let arr: [i32; 5] = [1, 2, 3, 4, 5];
- &str (строковый срез, неизменяемый)
- String (изменяемая строка)
let s1: &str = "Hello";
let s2: String = String::from("World");
- Статическая типизация с выводом типов
- Отсутствие неявного приведения типов
- Возможность создания пользовательских типов (структуры, перечисления)
- Поддержка обобщенных типов
Rust обеспечивает безопасность работы с памятью и предотвращает многие ошибки на этапе компиляции благодаря своей строгой системе типов.
В Rust существует несколько способов работы с типами данных и их преобразованием:
-
По умолчанию Rust не выполняет неявное приведение типов. Все преобразования должны быть явными.
-
Для явного приведения типов используется ключевое слово
as
:
let decimal = 65.4321_f32;
let integer = decimal as u8;
- Приведение типов с помощью
as
выполняется по определенным правилам и может приводить к усечению или насыщению значений.
- Позволяет выполнять безопасное приведение между совместимыми типами:
let x: i32 = 5;
let y: i64 = x as i64;
-
Работает для числовых типов, указателей, ссылок.
-
При приведении в меньший тип может происходить усечение значения.
- Для пользовательских типов можно реализовать трейты From и Into для выполнения преобразований:
impl From<i32> for MyType {
fn from(value: i32) -> Self {
// Реализация преобразования
}
}
let my_val: MyType = 5.into();
-
В Rust нет прямого аналога try-catch из других языков.
-
Вместо этого используется тип Result для обработки ошибок:
let result = some_fallible_operation()?;
- Оператор
?
позволяет пробрасывать ошибки наверх по стеку вызовов.
-
Для безопасного приведения типов рекомендуется использовать методы
from()
,into()
,try_from()
,try_into()
. -
Небезопасное приведение типов возможно с помощью
std::mem::transmute
, но его следует избегать. -
Для работы с указателями и сырыми указателями есть специальные методы приведения типов.
Таким образом, Rust предоставляет различные механизмы для безопасной работы с типами данных, делая акцент на явных преобразованиях и обработке ошибок.
В Rust поддерживаются следующие основные арифметические операции:
- Сложение:
+
- Вычитание:
-
- Умножение:
*
- Деление:
/
- Остаток от деления:
%
Пример использования:
let a = 10;
let b = 3;
println!("a + b = {}", a + b);
println!("a - b = {}", a - b);
println!("a * b = {}", a * b);
println!("a / b = {}", a / b);
println!("a % b = {}", a % b);
Rust также поддерживает составные операторы присваивания:
+=
: Сложение с присваиванием-=
: Вычитание с присваиванием*=
: Умножение с присваиванием/=
: Деление с присваиванием%=
: Остаток от деления с присваиванием
Пример:
let mut x = 5;
x += 3; // x теперь равно 8
x *= 2; // x теперь равно 16
- Операции выполняются с учетом типов данных. Например, деление целых чисел дает целочисленный результат.
- При переполнении в режиме отладки происходит паника, а в релизной версии - циклический перенос.
- Для работы с числами с плавающей точкой доступны дополнительные методы, такие как
sqrt()
для вычисления квадратного корня.
Rust обеспечивает безопасность типов и предотвращает неявные преобразования, что помогает избежать ошибок при выполнении арифметических операций.
В Rust поддерживаются следующие поразрядные операции:
&
: Поразрядное И (AND)|
: Поразрядное ИЛИ (OR)^
: Поразрядное исключающее ИЛИ (XOR)!
: Поразрядное НЕ (NOT)<<
: Сдвиг влево>>
: Сдвиг вправо
&=
: Поразрядное И с присваиванием|=
: Поразрядное ИЛИ с присваиванием^=
: Поразрядное исключающее ИЛИ с присваиванием<<=
: Сдвиг влево с присваиванием>>=
: Сдвиг вправо с присваиванием
let a = 5; // 0101 в двоичной системе
let b = 3; // 0011 в двоичной системе
println!("a & b = {}", a & b); // 0001 (1 в десятичной)
println!("a | b = {}", a | b); // 0111 (7 в десятичной)
println!("a ^ b = {}", a ^ b); // 0110 (6 в десятичной)
println!("!a = {}", !a); // 11111010 (-6 в десятичной для i8)
println!("a << 1 = {}", a << 1); // 1010 (10 в десятичной)
println!("a >> 1 = {}", a >> 1); // 0010 (2 в десятичной)
- Поразрядные операции выполняются только над целочисленными типами.
- Оператор
!
инвертирует все биты числа. - Сдвиг влево
<<
эквивалентен умножению на 2 в степени сдвига. - Сдвиг вправо
>>
эквивалентен целочисленному делению на 2 в степени сдвига.
Поразрядные операции часто используются для оптимизации кода, работы с флагами и битовыми масками, а также в низкоуровневом программировании.
Условные выражения в Rust позволяют управлять потоком выполнения программы на основе определенных условий. Основные типы условных выражений в Rust:
Базовый синтаксис выражения if:
if условие {
// код, выполняемый если условие истинно
} else if другое_условие {
// код, выполняемый если другое_условие истинно
} else {
// код, выполняемый если все предыдущие условия ложны
}
Особенности:
- Условие не требует круглых скобок
- Фигурные скобки обязательны даже для однострочных блоков кода
- else и else if блоки необязательны
В Rust if является выражением и может возвращать значение:
let число = if условие { 5 } else { 10 };
Все ветви if-выражения должны возвращать значения одного типа.
- Сравнение: ==, !=, <, >, <=, >=
- Логические: && (И), || (ИЛИ), ! (НЕ)
- Rust требует явного приведения типов в условиях
- Условия должны быть логического типа bool
- Нет тернарного оператора, вместо него используется if-выражение
let число = 5;
if число < 0 {
println!("{} отрицательное", число);
} else if число > 0 {
println!("{} положительное", число);
} else {
println!("{} равно нулю", число);
}
Условные выражения в Rust обеспечивают четкий и безопасный способ управления потоком программы, предотвращая многие распространенные ошибки благодаря строгой типизации и явным проверкам.
Конструкция match в Rust - это мощный инструмент для сопоставления с образцом и управления потоком выполнения программы. Вот ключевые аспекты match в Rust:
Базовый синтаксис match выглядит следующим образом:
match выражение {
шаблон1 => выражение1,
шаблон2 => выражение2,
// ...
_ => выражение_по_умолчанию,
}
Здесь выражение
сравнивается с каждым шаблоном
, и выполняется код, соответствующий первому совпавшему шаблону.
- Все возможные варианты должны быть обработаны. Если не все варианты перечислены явно, необходимо использовать шаблон
_
для обработки остальных случаев. - match является выражением и может возвращать значение.
- Шаблоны могут быть литералами, переменными, диапазонами и более сложными конструкциями.
Сопоставление с числами:
let x = 5;
match x {
1 => println!("Один"),
2 | 3 | 5 | 7 => println!("Простое число"),
13..=19 => println!("Подросток"),
_ => println!("Другое число"),
}
Сопоставление с перечислениями:
enum Color {
Red,
Green,
Blue,
}
let color = Color::Green;
match color {
Color::Red => println!("Красный"),
Color::Green => println!("Зеленый"),
Color::Blue => println!("Синий"),
}
- Связывание переменных: можно извлекать и связывать значения внутри шаблонов.
- Сопоставление диапазонов: можно использовать
..=
для включающих диапазонов. - Условия в шаблонах: можно добавлять условия с помощью
if
.
match в Rust предоставляет гибкий и безопасный способ обработки различных вариантов данных, обеспечивая исчерпывающую проверку всех возможных случаев.
В Rust существует три основных типа циклов:
Это бесконечный цикл, который выполняется до тех пор, пока не встретится оператор break:
loop {
println!("Бесконечный цикл");
if условие {
break;
}
}
Выполняется, пока условие истинно:
let mut x = 5;
while x > 0 {
println!("x = {}", x);
x -= 1;
}
Используется для итерации по диапазону значений или коллекции:
for i in 0..5 {
println!("i = {}", i);
}
- Циклы могут возвращать значение с помощью break:
let result = loop {
if условие {
break значение;
}
};
-
Ключевое слово continue используется для перехода к следующей итерации.
-
Циклы можно помечать метками для управления вложенными циклами:
'outer: for x in 0..5 {
'inner: for y in 0..5 {
if x * y > 10 {
break 'outer;
}
}
}
- Rust не имеет классического C-подобного цикла for с тремя выражениями.
Циклы в Rust обеспечивают безопасное и эффективное управление потоком выполнения программы, предотвращая многие распространенные ошибки благодаря строгой системе типов и проверкам на этапе компиляции.
Функции в Rust являются ключевым элементом языка и имеют несколько важных особенностей:
Функции в Rust объявляются с помощью ключевого слова fn
:
fn имя_функции(параметры) -> тип_возвращаемого_значения {
// тело функции
}
Например:
fn sum(a: i32, b: i32) -> i32 {
a + b
}
Возвращаемое значение
- Тип возвращаемого значения указывается после
->
. - Последнее выражение в функции неявно возвращается без использования
return
. - Если функция ничего не возвращает, можно опустить
-> ()
или использовать-> ()
явно.
Параметры
- Параметры функции должны иметь явно указанный тип.
- Rust не поддерживает параметры по умолчанию.
Выражения и операторы
- Rust - язык, ориентированный на выражения.
- Большинство конструкций в Rust являются выражениями и возвращают значение.
Функции вызываются по имени с передачей аргументов в круглых скобках:
let result = sum(5, 3);
Rust поддерживает методы - функции, связанные с конкретным типом:
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
Функции в Rust обеспечивают безопасность и эффективность благодаря строгой системе типов и проверкам на этапе компиляции, что делает их мощным инструментом для структурирования кода.
Rust поддерживает анонимные функции (замыкания):
let add_one = |x| x + 1;
Замыкания могут захватывать значения из окружающей области видимости. Вот ключевые особенности замыканий в Rust:
Замыкания определяются с помощью вертикальных черт ||
:
let closure = |параметры| { тело_замыкания };
Например:
let add_one = |x| x + 1;
Замыкания могут захватывать переменные из окружающего контекста тремя способами:
- Заимствование неизменяемой ссылки
- Заимствование изменяемой ссылки
- Получение владения
Rust автоматически определяет способ захвата на основе того, как переменные используются в теле замыкания.
Существует три трейта для замыканий:
Fn
- замыкание захватывает по неизменяемой ссылкеFnMut
- замыкание захватывает по изменяемой ссылкеFnOnce
- замыкание получает владение захваченными значениями
Замыкания часто используются:
- В качестве аргументов функций
- С итераторами
- Для отложенных вычислений
Например:
let numbers = vec![1, 2, 3];
numbers.iter().map(|x| x * 2).collect();
Компилятор Rust может выводить типы параметров и возвращаемого значения замыкания, что делает их синтаксис более компактным по сравнению с обычными функциями.
Замыкания в Rust обеспечивают гибкий и мощный механизм для создания анонимных функций, которые могут захватывать свое окружение, что делает их полезным инструментом для функционального программирования и обработки данных.
Параметры функций в Rust имеют несколько важных особенностей:
Параметры функции объявляются в круглых скобках после имени функции:
fn имя_функции(параметр1: Тип1, параметр2: Тип2) {
// тело функции
}
- Тип каждого параметра должен быть явно указан.
- Это сделано намеренно, чтобы компилятор мог проверять типы и выдавать более полезные сообщения об ошибках.
- По умолчанию параметры передаются по значению (копируются).
- Для передачи по ссылке используются ссылки (&) или изменяемые ссылки (&mut).
Параметры функции могут быть шаблонами, что позволяет использовать более сложные конструкции:
fn функция((x, y): (i32, i32)) {
// использование x и y
}
Rust не поддерживает вариативные параметры напрямую, но их можно эмулировать с помощью макросов или слайсов.
Функции можно передавать в качестве параметров другим функциям:
fn применить(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
Помимо обычных функций, в качестве параметров можно передавать замыкания, используя трейты Fn, FnMut и FnOnce.
Параметры функций в Rust обеспечивают безопасность типов и гибкость, позволяя создавать четкие и надежные интерфейсы функций.
В Rust существует несколько способов возвращения значения из функции:
Наиболее распространенный способ - это неявное возвращение последнего выражения в теле функции:
fn add(a: i32, b: i32) -> i32 {
a + b // Неявно возвращается сумма a и b
}
Обратите внимание на отсутствие точки с запятой в конце выражения.
Можно также использовать ключевое слово return
для явного возврата значения:
fn check_positive(x: i32) -> bool {
if x > 0 {
return true;
}
false
}
return
часто используется для раннего выхода из функции.
Значения можно возвращать из блоков if
, match
и других:
fn abs(x: i32) -> i32 {
if x >= 0 {
x
} else {
-x
}
}
- Тип возвращаемого значения указывается после стрелки
->
в сигнатуре функции. - Если функция ничего не возвращает, используется тип
()
(unit type), который можно опустить. - Rust позволяет возвращать значения без использования точки с запятой, что делает код более лаконичным.
Выбор способа возврата значения зависит от конкретной ситуации и стиля кодирования, но неявное возвращение последнего выражения считается идиоматичным в Rust.
Константы в Rust представляют собой неизменяемые значения, которые определяются на этапе компиляции и доступны в течение всего времени выполнения программы. Вот ключевые особенности констант в Rust:
Константы объявляются с помощью ключевого слова const
:
const PI: f32 = 3.14159;
Особенности объявления:
- Тип константы должен быть явно указан
- Имена констант обычно пишутся в верхнем регистре (SCREAMING_SNAKE_CASE)
- Константы могут быть объявлены в любой области видимости, включая глобальную
- Константы всегда неизменяемы, в отличие от переменных, которые могут быть объявлены как изменяемые (
mut
) - Значение константы должно быть известно на этапе компиляции
- Константы встраиваются в код в местах использования, а не хранятся в определенном месте памяти
Константы часто используются для:
- Определения математических констант (π, e и т.д.)
- Задания конфигурационных значений программы
- Определения размеров массивов и других структур данных
- Константы могут содержать только константные выражения
- Допустимы сложные константные выражения, включая арифметические операции и вызовы
const fn
- Константы могут ссылаться на другие константы, но не на статические переменные (
static
)
В Rust также есть статические переменные (static
), которые отличаются от констант:
static
переменные имеют фиксированный адрес в памятиstatic mut
переменные могут быть изменяемыми, но работа с ними считается небезопасной
Константы предпочтительнее в большинстве случаев из-за их безопасности и оптимизаций, которые может выполнить компилятор.
В Rust функция может представлять собой отдельный тип данных. Тип функции состоит из типов её параметров и типа возвращаемого значения.
Тип функции записывается следующим образом:
fn(тип_параметра1, тип_параметра2, ...) -> тип_возвращаемого_значения
- Функция без параметров, ничего не возвращающая:
let f: fn() = || { println!("Hello"); };
- Функция с параметрами:
let multiply: fn(i32, i32) -> i32 = |a, b| a * b;
- Присваивание обычной функции переменной:
fn add(a: i32, b: i32) -> i32 {
a + b
}
let operation: fn(i32, i32) -> i32 = add;
- Тип функции позволяет использовать функции как значения, передавать их в качестве аргументов или возвращать из других функций.
- При объявлении переменной с типом функции можно присваивать ей как именованные функции, так и замыкания.
- Использование типа функции делает код более гибким и позволяет реализовывать функции высшего порядка.
Объявление переменных с типом функции особенно полезно при работе с коллбэками, функциями высшего порядка и при реализации различных паттернов функционального программирования в Rust.
В Rust функции могут быть использованы как параметры и результаты других функций, что позволяет создавать функции высшего порядка. Это мощная возможность для создания гибкого и переиспользуемого кода.
Чтобы передать функцию в качестве параметра, нужно указать её тип в сигнатуре принимающей функции:
fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn double(x: i32) -> i32 {
x * 2
}
fn main() {
let result = apply(double, 5);
println!("Result: {}", result); // Выведет: Result: 10
}
Функция также может возвращать другую функцию:
fn create_adder(y: i32) -> impl Fn(i32) -> i32 {
move |x| x + y
}
fn main() {
let add_five = create_adder(5);
println!("Result: {}", add_five(10)); // Выведет: Result: 15
}
В этом примере impl Fn(i32) -> i32
означает, что функция возвращает некоторый тип, реализующий трейт Fn(i32) -> i32
.
Часто вместо обычных функций используются замыкания:
fn transform<F>(values: Vec<i32>, f: F) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
values.into_iter().map(f).collect()
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let squared = transform(numbers, |x| x * x);
println!("Squared: {:?}", squared); // Выведет: Squared: [1, 4, 9, 16, 25]
}
Использование функций как параметров и результатов других функций позволяет создавать более абстрактный и переиспользуемый код, что особенно полезно при реализации различных паттернов функционального программирования.
Кортежи в Rust представляют собой коллекции, которые могут хранить значения разных типов. Вот основные характеристики и особенности кортежей в Rust:
Кортежи создаются с помощью круглых скобок:
let tuple = (1, "hello", 3.14);
Типы элементов кортежа могут быть разными, и их количество фиксировано при создании.
К элементам кортежа можно обращаться по индексу, начиная с 0:
let x = tuple.0; // Первый элемент
let y = tuple.1; // Второй элемент
Кортежи можно деструктурировать, присваивая их элементы отдельным переменным:
let (a, b, c) = tuple;
Кортежи часто используются для возврата нескольких значений из функции:
fn return_tuple() -> (i32, String) {
(42, String::from("hello"))
}
Rust поддерживает кортежные структуры - структуры без именованных полей:
struct Point(i32, i32);
let origin = Point(0, 0);
- Кортежи могут содержать другие кортежи
- Для создания кортежа с одним элементом используется запятая:
(5,)
- Кортежи можно сравнивать, если их элементы сравнимы
Кортежи в Rust предоставляют удобный способ группировки разнородных данных и часто используются для кратковременного хранения связанных значений или множественного возврата из функций.
Массивы в Rust представляют собой коллекции элементов одного типа фиксированного размера. Вот ключевые особенности массивов в Rust:
Массивы объявляются с помощью квадратных скобок:
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
Здесь [i32; 5]
означает массив из 5 элементов типа i32.
Можно также инициализировать массив одним значением:
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
Доступ к элементам осуществляется по индексу:
let first = numbers[0];
let second = numbers[1];
Размер массива является частью его типа и должен быть известен на этапе компиляции. Длину массива можно получить методом len()
:
let length = numbers.len(); // 5
Для перебора элементов массива можно использовать цикл for:
for number in numbers.iter() {
println!("{}", number);
}
Многомерные массивы представляют собой массивы массивов:
let matrix: [[i32; 3]; 2] = [[1, 2, 3], [4, 5, 6]];
- Массивы в Rust имеют фиксированный размер и хранятся в стеке.
- При выходе за границы массива происходит паника во время выполнения.
- Массивы реализуют ряд трейтов, включая
Debug
,Clone
,Copy
(если тип элементов реализует эти трейты).
Массивы в Rust обеспечивают эффективное хранение и доступ к данным, но из-за фиксированного размера менее гибки, чем векторы. Для динамических коллекций обычно используются векторы (Vec<T>
).
Структуры в Rust - это пользовательские типы данных, позволяющие группировать связанные значения. Вот ключевые аспекты структур в Rust:
Структуры объявляются с помощью ключевого слова struct
:
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
Экземпляры структур создаются путем указания конкретных значений для каждого поля:
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
Доступ к полям осуществляется через точечную нотацию:
println!("{}", user1.email);
- Классические структуры с именованными полями
- Кортежные структуры без именованных полей
- Unit-подобные структуры без полей
Методы определяются в блоке impl
:
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
- Структуры могут содержать различные типы данных
- Поддерживают деструктуризацию
- Могут использоваться в качестве параметров и возвращаемых значений функций
- Поддерживают обобщённые типы и трейты
Структуры в Rust предоставляют мощный механизм для организации и группировки связанных данных, что делает код более читаемым и поддерживаемым.
Структуры-кортежи в Rust - это особый вид структур, которые похожи на кортежи, но имеют имя типа. Вот ключевые особенности структур-кортежей:
Структуры-кортежи определяются следующим образом:
struct Color(i32, i32, i32);
struct Point(f64, f64);
Здесь Color
и Point
- имена типов, а в скобках указаны типы их компонентов.
Экземпляры структур-кортежей создаются аналогично обычным кортежам:
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
К элементам структуры-кортежа можно обращаться по индексу, как в обычных кортежах:
let red = black.0;
let x = origin.0;
Структуры-кортежи можно деструктурировать:
let Color(r, g, b) = black;
Для структур-кортежей также можно определять методы:
impl Point {
fn distance(&self, other: &Point) -> f64 {
let dx = self.0 - other.0;
let dy = self.1 - other.1;
(dx * dx + dy * dy).sqrt()
}
}
- Структуры-кортежи полезны, когда нужно дать имя кортежу, но не требуется именовать отдельные поля.
- Они часто используются для простых группировок данных.
- Структуры-кортежи могут быть публичными (
pub
), что делает их удобными для создания простых публичных API.
Структуры-кортежи в Rust предоставляют компактный способ группировки данных, сочетая простоту кортежей с возможностью создания именованных типов.
Перечисления (enums) в Rust - это пользовательские типы данных, которые позволяют определить значение как одно из нескольких возможных вариантов. Основные характеристики перечислений в Rust:
Перечисления объявляются с помощью ключевого слова enum
:
enum IpAddr {
V4(String),
V6(String),
}
Варианты перечисления могут:
- Быть пустыми (без данных)
- Содержать данные различных типов
- Иметь именованные поля (как структуры)
- Содержать кортежи
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
Создание экземпляра перечисления:
let home = IpAddr::V4(String::from("127.0.0.1"));
Для перечислений можно определять методы с помощью блока impl
:
impl Message {
fn call(&self) {
// Реализация метода
}
}
Для работы с перечислениями часто используется конструкция match
:
match ip_addr {
IpAddr::V4(addr) => println!("IPv4: {}", addr),
IpAddr::V6(addr) => println!("IPv6: {}", addr),
}
Перечисления в Rust предоставляют мощный способ моделирования данных, позволяя создавать сложные типы и эффективно работать с ними.
Последовательности в Rust представляют собой диапазоны значений и часто используются для итерации и получения части набора элементов. Вот ключевые аспекты последовательностей в Rust:
Последовательности создаются с помощью оператора ..
:
let numbers = 1..9; // Последовательность от 1 до 8 (9 не включается)
Для включения конечного значения используется оператор ..=
:
let numbers = 1..=9; // Последовательность от 1 до 9 (включительно)
Последовательности часто применяются в циклах for
:
for num in 1..5 {
println!("{}", num); // Выведет числа от 1 до 4
}
Последовательности удобны для извлечения части массива или вектора:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8];
let slice = &numbers[1..5]; // [2, 3, 4, 5]
- Последовательности ленивы и не создают коллекцию в памяти
- Могут быть бесконечными (например,
1..
) - Поддерживают обратный порядок (
(1..5).rev()
) - Реализуют трейт
Iterator
, что позволяет использовать методы вродеmap
,filter
и др.
Последовательности в Rust предоставляют эффективный способ работы с диапазонами значений, особенно полезный при итерации и извлечении подмножеств из коллекций.
Сопоставление с образцом (pattern matching) для диапазона значений в Rust - это мощный инструмент, позволяющий проверять, попадает ли значение в определенный диапазон. Вот ключевые аспекты этой возможности:
Для сопоставления с диапазоном используется оператор ..=
внутри конструкции match
или if let
:
let x = 5;
match x {
1..=5 => println!("от 1 до 5"),
6..=10 => println!("от 6 до 10"),
_ => println!("что-то другое"),
}
match
позволяет проверять несколько диапазонов:
let grade = 'B';
match grade {
'A'..='C' => println!("Хорошая оценка"),
'D'..='F' => println!("Нужно улучшить"),
_ => println!("Недопустимая оценка"),
}
if let
можно использовать для проверки одного диапазона:
let age = 30;
if let 18..=65 = age {
println!("Трудоспособный возраст");
}
- Работает с числовыми типами и символами (
char
) - Можно комбинировать с другими паттернами
- Удобно для обработки различных диапазонов значений
Сопоставление с образцом для диапазонов в Rust предоставляет элегантный способ обработки различных интервалов значений, делая код более читаемым и выразительным.
Сопоставление с образцом (pattern matching) для кортежей в Rust - это мощный инструмент для работы со структурированными данными. Вот ключевые аспекты этой возможности:
Для сопоставления с кортежем используется конструкция match
или if let
:
let tuple = (1, "hello", 3.14);
match tuple {
(1, s, _) => println!("Первый элемент 1, второй: {}", s),
(x, _, z) => println!("Первый: {}, третий: {}", x, z),
}
match
позволяет проверять различные варианты кортежей:
let point = (0, 0);
match point {
(0, 0) => println!("Начало координат"),
(0, y) => println!("На оси Y: {}", y),
(x, 0) => println!("На оси X: {}", x),
(x, y) => println!("Точка: ({}, {})", x, y),
}
if let
удобно использовать для проверки конкретной структуры кортежа:
let tuple = (1, "hello", 3.14);
if let (1, s, _) = tuple {
println!("Кортеж начинается с 1, второй элемент: {}", s);
}
Сопоставление с образцом позволяет легко деструктурировать кортежи:
let (x, y, z) = (1, "hello", 3.14);
println!("x: {}, y: {}, z: {}", x, y, z);
Можно игнорировать отдельные элементы кортежа с помощью _
:
let (_, second, _) = (1, "hello", 3.14);
println!("Второй элемент: {}", second);
Сопоставление работает и с вложенными кортежами:
let nested = ((1, 2), (3, 4));
match nested {
((a, b), (c, d)) => println!("a: {}, b: {}, c: {}, d: {}", a, b, c, d),
}
Сопоставление с образцом для кортежей в Rust предоставляет гибкий и выразительный способ работы со структурированными данными, позволяя легко извлекать и проверять отдельные элементы кортежей.
Сопоставление с образцом для структур в Rust - это мощный инструмент для работы со структурированными данными. Вот основные аспекты этой возможности:
Для сопоставления со структурой используется следующий синтаксис:
struct Point {
x: i32,
y: i32,
}
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("На оси X: {}", x),
Point { x: 0, y } => println!("На оси Y: {}", y),
Point { x, y } => println!("Точка: ({}, {})", x, y),
}
Сопоставление с образцом позволяет легко деструктурировать структуры:
let Point { x, y } = p;
Можно сопоставлять только часть полей структуры:
match p {
Point { x: 10, .. } => println!("X равен 10"),
Point { .. } => println!("Любая другая точка"),
}
При деструктуризации можно присваивать полям новые имена:
let Point { x: a, y: b } = p;
Если имена переменных совпадают с именами полей структуры, можно использовать сокращенную запись:
let Point { x, y } = p;
Сопоставление с образцом для структур в Rust предоставляет гибкий и выразительный способ работы со структурированными данными, позволяя легко извлекать и проверять отдельные поля структур.
Сопоставление с образцом в Rust с использованием символа @
называется связыванием (binding) и позволяет создавать переменные, содержащие значения, соответствующие определенному шаблону. Вот ключевые аспекты этой возможности:
Синтаксис связывания выглядит следующим образом:
идентификатор @ шаблон
- Связывание с конкретным значением:
let x = 1;
match x {
e @ 1..=5 => println!("Значение от 1 до 5: {}", e),
_ => println!("Другое значение"),
}
- Связывание в структурах:
struct Point { x: i32, y: i32 }
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: y @ 1..=5 } => println!("y от 1 до 5: {}", y),
Point { x: x @ 1..=5, y } => println!("x от 1 до 5: {}", x),
_ => println!("Другая точка"),
}
- Связывание с частью массива:
let arr = [1, 2, 3, 4, 5];
match arr {
[1, rest @ ..] => println!("Начинается с 1, остаток: {:?}", rest),
_ => println!("Не начинается с 1"),
}
- Позволяет одновременно проверять значение на соответствие шаблону и сохранять его в переменную
- Удобно для извлечения значений, соответствующих сложным условиям
- Может использоваться в различных контекстах: с числами, структурами, массивами и т.д.
Связывание с помощью @
в Rust предоставляет мощный инструмент для работы со сложными шаблонами, позволяя эффективно извлекать и использовать данные в процессе сопоставления с образцом.