Skip to content

Instantly share code, notes, and snippets.

@dmitry-osin
Last active January 10, 2025 08:46
Show Gist options
  • Save dmitry-osin/e14f33041b89ebd28d8cd616f89d39d2 to your computer and use it in GitHub Desktop.
Save dmitry-osin/e14f33041b89ebd28d8cd616f89d39d2 to your computer and use it in GitHub Desktop.
Шпаргалка по языку Rust

Шпаргалка по языку Rust

Введение

Программа на 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 существует несколько способов работы с типами данных и их преобразованием:

Присвоение и конверсия типов

  • По умолчанию Rust не выполняет неявное приведение типов. Все преобразования должны быть явными.

  • Для явного приведения типов используется ключевое слово as:

let decimal = 65.4321_f32;
let integer = decimal as u8;
  • Приведение типов с помощью as выполняется по определенным правилам и может приводить к усечению или насыщению значений.

Ключевое слово as

  • Позволяет выполнять безопасное приведение между совместимыми типами:
let x: i32 = 5;
let y: i64 = x as i64;
  • Работает для числовых типов, указателей, ссылок.

  • При приведении в меньший тип может происходить усечение значения.

Трейты From и Into

  • Для пользовательских типов можно реализовать трейты From и Into для выполнения преобразований:
impl From<i32> for MyType {
    fn from(value: i32) -> Self {
        // Реализация преобразования
    }
}

let my_val: MyType = 5.into();

Ключевое слово try

  • В Rust нет прямого аналога try-catch из других языков.

  • Вместо этого используется тип Result для обработки ошибок:

let result = some_fallible_operation()?;
  • Оператор ? позволяет пробрасывать ошибки наверх по стеку вызовов.

Приведение типов

  • Для безопасного приведения типов рекомендуется использовать методы from(), into(), try_from(), try_into().

  • Небезопасное приведение типов возможно с помощью std::mem::transmute, но его следует избегать.

  • Для работы с указателями и сырыми указателями есть специальные методы приведения типов.

Таким образом, Rust предоставляет различные механизмы для безопасной работы с типами данных, делая акцент на явных преобразованиях и обработке ошибок.

Арифметические операции в 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

Особенности арифметических операций в Rust

  • Операции выполняются с учетом типов данных. Например, деление целых чисел дает целочисленный результат.
  • При переполнении в режиме отладки происходит паника, а в релизной версии - циклический перенос.
  • Для работы с числами с плавающей точкой доступны дополнительные методы, такие как sqrt() для вычисления квадратного корня.

Rust обеспечивает безопасность типов и предотвращает неявные преобразования, что помогает избежать ошибок при выполнении арифметических операций.

Поразрядные операции в 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:

if условие {
    // код, выполняемый если условие истинно
} else if другое_условие {
    // код, выполняемый если другое_условие истинно
} else {
    // код, выполняемый если все предыдущие условия ложны
}

Особенности:

  • Условие не требует круглых скобок
  • Фигурные скобки обязательны даже для однострочных блоков кода
  • else и else if блоки необязательны

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 как альтернатива if

Конструкция match в Rust - это мощный инструмент для сопоставления с образцом и управления потоком выполнения программы. Вот ключевые аспекты match в Rust:

Синтаксис и основы

Базовый синтаксис match выглядит следующим образом:

match выражение {
    шаблон1 => выражение1,
    шаблон2 => выражение2,
    // ...
    _ => выражение_по_умолчанию,
}

Здесь выражение сравнивается с каждым шаблоном, и выполняется код, соответствующий первому совпавшему шаблону.

Особенности match

  • Все возможные варианты должны быть обработаны. Если не все варианты перечислены явно, необходимо использовать шаблон _ для обработки остальных случаев.
  • 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 существует три основных типа циклов:

Цикл loop

Это бесконечный цикл, который выполняется до тех пор, пока не встретится оператор break:

loop {
    println!("Бесконечный цикл");
    if условие {
        break;
    }
}

Цикл while

Выполняется, пока условие истинно:

let mut x = 5;
while x > 0 {
    println!("x = {}", x);
    x -= 1;
}

Цикл for

Используется для итерации по диапазону значений или коллекции:

for i in 0..5 {
    println!("i = {}", i);
}

Особенности циклов в Rust

  • Циклы могут возвращать значение с помощью 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
}

Особенности функций в Rust

Возвращаемое значение

  • Тип возвращаемого значения указывается после ->.
  • Последнее выражение в функции неявно возвращается без использования 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

Можно также использовать ключевое слово 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)

Константы vs статические переменные

В Rust также есть статические переменные (static), которые отличаются от констант:

  • static переменные имеют фиксированный адрес в памяти
  • static mut переменные могут быть изменяемыми, но работа с ними считается небезопасной

Константы предпочтительнее в большинстве случаев из-за их безопасности и оптимизаций, которые может выполнить компилятор.

Тип функции

В Rust функция может представлять собой отдельный тип данных. Тип функции состоит из типов её параметров и типа возвращаемого значения.

Синтаксис типа функции

Тип функции записывается следующим образом:

fn(тип_параметра1, тип_параметра2, ...) -> тип_возвращаемого_значения

Примеры объявления переменных с типом функции

  1. Функция без параметров, ничего не возвращающая:
let f: fn() = || { println!("Hello"); };
  1. Функция с параметрами:
let multiply: fn(i32, i32) -> i32 = |a, b| a * b;
  1. Присваивание обычной функции переменной:
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);

Типы структур

  1. Классические структуры с именованными полями
  2. Кортежные структуры без именованных полей
  3. 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

match позволяет проверять несколько диапазонов:

let grade = 'B';

match grade {
    'A'..='C' => println!("Хорошая оценка"),
    'D'..='F' => println!("Нужно улучшить"),
    _ => println!("Недопустимая оценка"),
}

Применение в if let

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

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

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) и позволяет создавать переменные, содержащие значения, соответствующие определенному шаблону. Вот ключевые аспекты этой возможности:

Основной синтаксис

Синтаксис связывания выглядит следующим образом:

идентификатор @ шаблон

Примеры использования

  1. Связывание с конкретным значением:
let x = 1;
match x {
    e @ 1..=5 => println!("Значение от 1 до 5: {}", e),
    _ => println!("Другое значение"),
}
  1. Связывание в структурах:
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!("Другая точка"),
}
  1. Связывание с частью массива:
let arr = [1, 2, 3, 4, 5];
match arr {
    [1, rest @ ..] => println!("Начинается с 1, остаток: {:?}", rest),
    _ => println!("Не начинается с 1"),
}

Особенности

  • Позволяет одновременно проверять значение на соответствие шаблону и сохранять его в переменную
  • Удобно для извлечения значений, соответствующих сложным условиям
  • Может использоваться в различных контекстах: с числами, структурами, массивами и т.д.

Связывание с помощью @ в Rust предоставляет мощный инструмент для работы со сложными шаблонами, позволяя эффективно извлекать и использовать данные в процессе сопоставления с образцом.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment