Rust часто описывают через безопасность памяти и borrow checker, но повседневная работа начинается с другого: создать проект, разложить код по модулям, подключить зависимость и запустить тесты. Именно эти навыки нужны в первый день, а не знание тонкостей лайфтаймов. Ниже — практический маршрут, который можно повторить в терминале за вечер.

Установка инструментов

Официальный способ — rustup. Он ставит компилятор, cargo и стандартную документацию, а также позволяет переключать версии. Не смешивайте пакет из репозитория дистрибутива с rustup: разные пути к бинарникам приводят к путанице.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
rustc --version
cargo --version

Если политика компании запрещает скачивать установщик, возьмите пакет rustc и cargo из репозитория, но проверьте версию: старый компилятор не соберёт проект с новой редакцией. Для учебных задач хватит и его, для рабочих — лучше rustup.

Создание проекта

Cargo создаёт каталог с манифестом, точкой входа и git-репозиторием. Флаг —bin означает исполняемую программу, —lib — библиотеку. Для первого знакомства достаточно бинарного проекта.

cargo new notes --bin
cd notes
cargo run
cargo tree

Сборка кладёт результат в target/debug. Каталог не нужно добавлять в git: cargo new уже создал .gitignore. Для проверки синтаксиса без запуска есть cargo check — он быстрее полной сборки и экономит время при частых правках.

Модули и видимость

Модуль — это пространство имён и граница видимости. Файл src/store.rs становится модулем store, если объявить его в src/main.rs через mod store. Всё, что должно быть доступно снаружи, помечается pub. По умолчанию элементы приватны, и это защищает от случайных связей.

mod store;

use store::Store;

fn main() {
    let mut s = Store::new();
    s.add("купить хлеб");
    println!("заметок: {}", s.len());
}

Сам модуль хранит структуру и методы. Обратите внимание на &str в аргументе: функция принимает строковый срез и не забирает владение, поэтому вызывающий код сохраняет свою строку.

#[derive(Debug)]
pub struct Note {
    pub text: String,
}

pub struct Store {
    notes: Vec<Note>,
}

impl Store {
    pub fn new() -> Self {
        Self { notes: Vec::new() }
    }

    pub fn add(&mut self, text: &str) {
        self.notes.push(Note { text: text.to_string() });
    }

    pub fn len(&self) -> usize {
        self.notes.len()
    }
}

impl Default for Store {
    fn default() -> Self {
        Self::new()
    }
}

Когда файлов становится много, сгруппируйте их в каталог с store/mod.rs и подмодулями. Главное — не держать всю программу в main.rs: через пару недель его невозможно читать.

Зависимости

Сторонние крейты подключаются в Cargo.toml. Команда cargo add подбирает актуальную версию и сама правит манифест. Фича derive у serde включает генерацию кода для сериализации; без неё придётся писать реализации вручную.

cargo add serde --features derive
cargo add serde_json
cargo build

Файл Cargo.lock фиксирует точные версии и коммитится для приложений. Для библиотек его обычно не включают. Не указывайте звёздочку в версии зависимости: сборка станет невоспроизводимой, а обновление — сюрпризом.

[package]
name = "notes"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Редакция 2024 поддерживается актуальными компиляторами и включает изменения в захвате переменных и обработке unsafe. Если проект старый, менять edition без причины не стоит: возможны предупреждения и правки.

Тесты

Модульные тесты живут рядом с кодом в блоке #[cfg(test)]. Атрибут означает, что код компилируется только при тестировании. Макрос assert_eq! сравнивает значения и печатает разницу при ошибке.

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_increases_len() {
        let mut s = Store::new();
        s.add("тест");
        assert_eq!(s.len(), 1);
    }

    #[test]
    fn add_keeps_text() {
        let mut s = Store::new();
        s.add("молоко");
        assert_eq!(s.notes[0].text, "молоко");
    }
}

Запуск cargo test собирает тестовый профиль и выполняет все функции с атрибутом test. Фильтр по имени помогает прогнать один сценарий: cargo test add_. Флаг — —nocapture показывает вывод println!, который по умолчанию скрыт.

cargo test
cargo test add_ -- --nocapture
cargo test --release

Интеграционные тесты кладут в каталог tests/ — каждый файл там компилируется как отдельный крейт и использует публичный API. Документационные примеры в комментариях /// тоже проверяются, если запустить cargo test —doc.

Обработка ошибок без паники

В прикладном коде вместо unwrap лучше возвращать Result и использовать оператор ?. Он пробрасывает ошибку вызывающему, сохраняя контекст. Паника уместна только там, где нарушен инвариант программы.

use std::fs;

fn count_lines(path: &str) -> std::io::Result<usize> {
    let text = fs::read_to_string(path)?;
    Ok(text.lines().count())
}

Для своих типов ошибок удобно завести enum и реализовать Display, либо взять thiserror. Для приложений подходит anyhow: он добавляет контекст и упрощает main, возвращающий Result. Главное — не глотать ошибку пустым match.

Форматирование и линтер

rustfmt приводит код к единому стилю, clippy ищет подозрительные конструкции. Оба инструмента ставятся через rustup component. В команде clippy флаг -D warnings превращает предупреждения в ошибку сборки — удобно для CI.

cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo build --release

Release-сборка включает оптимизации и занимает больше времени. Бинарник оказывается в target/release. Для распространения полезно проверить его через ldd и убедиться, что нет неожиданных динамических зависимостей.

Полезно заглянуть в cargo doc —open: локальная документация собирается по вашим крейтам и зависимостям, без интернета. Если проект обрастает функциями, включайте их через feature flags в Cargo.toml и проверяйте сборку с —no-default-features.

Рабочий цикл

  • Пишите маленькими шагами и запускайте cargo check после каждой правки.
  • Сначала тест, потом реализация — особенно для разбора форматов и граничных случаев.
  • Держите модули по смыслу, а не по типу: store, cli, config.
  • Не добавляйте зависимость ради одной функции — стандартная библиотека часто достаточна.
  • Перед коммитом прогоняйте fmt, clippy и test.

Итог

Cargo снимает рутину сборки и управления зависимостями, модули помогают держать код читаемым, а встроенные тесты делают проверку частью обычного цикла. Освойте четыре команды — cargo run, cargo test, cargo fmt, cargo clippy — и вы сможете уверенно вести небольшой проект. Остальное приходит с практикой: borrow checker перестаёт мешать, когда код разбит на маленькие функции с ясными границами ответственности.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте