Avatar ·

Как настроить автоматическую перекомпиляцию Rust-проекта при изменении файлов?

Всем привет! Я новичок в Rust и использую Cargo для сборки проектов. Меня очень раздражает, что после каждого изменения в коде мне приходится вручную запускать cargo build или cargo run. Я привык к автоматической перекомпиляции в других языках (например, в Node.js с nodemon или в Python с --reload).

Вот мой типичный workflow:

// main.rs
fn main() {
    println!("Hello, world!");
}

Я редактирую файл, сохраняю его, потом бегу в терминал и пишу cargo run. Это отнимает время и нарушает поток мыслей. Я слышал, что есть инструменты вроде cargo-watch, но не уверен, как их правильно установить и использовать.

Подскажите, пожалуйста, как настроить автоматическую перекомпиляцию и перезапуск программы при изменении исходных файлов? Хотелось бы получить решение, которое работает стабильно, не требует сложной настройки и подходит как для маленьких учебных проектов, так и для более серьёзных приложений.

Заранее спасибо за помощь!

<<>>

Установка и использование cargo-watch

Самый популярный и надёжный способ автоматической перекомпиляции в Rust — это утилита cargo-watch. Она отслеживает изменения в файлах вашего проекта и автоматически запускает указанную команду Cargo.

Установка

Установите cargo-watch через Cargo (один раз):

cargo install cargo-watch

Базовое использование

Запустите в корневой папке вашего проекта:

cargo watch -x run

Теперь при каждом сохранении файла Cargo автоматически перекомпилирует и запустит вашу программу. Флаг -x означает "execute" (выполнить команду).

Продвинутые примеры

  • Только компиляция без запуска:
    cargo watch -x build
  • Запуск тестов при изменениях:
    cargo watch -x test
  • Несколько команд последовательно:
    cargo watch -x check -x test -x run

    Сначала проверка (check), потом тесты, потом запуск.

  • Игнорирование определённых файлов:
    cargo watch -x run -i "target/*,*.log"
  • Задержка перед перезапуском (полезно при сохранении больших файлов):
    cargo watch -x run --delay 2

Почему это работает?

cargo-watch использует системные утилиты для отслеживания файловой системы (inotify на Linux, FSEvents на macOS, ReadDirectoryChanges на Windows). Он реагирует на любые изменения в папке проекта, кроме указанных в игнорировании. Это гораздо эффективнее, чем постоянный опрос файловой системы.

Важные советы

  • Не используйте cargo-watch в продакшене — это инструмент для разработки.
  • Если программа зависает (например, ожидает ввода), cargo-watch не сможет перезапустить её автоматически. Используйте Ctrl+C для остановки.
  • Для веб-приложений рассмотрите systemfd и listenfd (из крейта listenfd) для горячей перезагрузки без потери порта.
  • Альтернатива: cargo watch -c -x run (флаг -c очищает экран перед каждым запуском).

Пример полного workflow

# Установка
cargo install cargo-watch

# Разработка
cd my_project
cargo watch -x run

# Теперь просто редактируйте код — всё перекомпилируется автоматически!

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

<<>>

Использование cargo-watch с горячей перезагрузкой для веб-приложений

Если вы разрабатываете веб-сервер на Rust (например, с Actix-web, Rocket или Axum), просто cargo watch -x run может быть неудобен, так как при перезапуске теряется TCP-порт. Решение — использовать systemfd и listenfd.

Установка

cargo install systemfd cargo-watch

Настройка сервера

Добавьте в Cargo.toml:

[dependencies]
listenfd = "1.0"

Измените код сервера для использования переданного файлового дескриптора:

use listenfd::ListenFd;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let mut listenfd = ListenFd::from_env();
    let server = HttpServer::new(|| App::new().route("/", web::get().to(|| HttpResponse::Ok())));
    
    let server = if let Some(l) = listenfd.take_tcp_listener(0)? {
        server.listen(l)?
    } else {
        server.bind("127.0.0.1:8080")?
    };
    
    server.run().await
}

Запуск

systemfd --no-pid -s http::8080 -- cargo watch -x run

Теперь при изменении файлов сервер перезапускается без потери порта и без прерывания соединений. Это особенно полезно при разработке API.