Avatar ·

Пошаговый отладчик для Rust: как дебажить код в 2024 году?

Всем привет! Я пишу на Rust уже несколько месяцев, но никак не могу привыкнуть к отладке. В Python я просто ставил `import pdb; pdb.set_trace()` и спокойно шагал по коду. В Rust же я чувствую себя слепым котёнком. Вот конкретная проблема. У меня есть функция, которая обрабатывает заказы: ```rust fn process_orders(orders: Vec) -> Vec { orders .into_iter() .filter(|o| o.status == OrderStatus::Pending) .map(|o| { let discount = calculate_discount(&o); let total = o.amount - discount; ProcessedOrder { id: o.id, final_price: total, items_count: o.items.len(), } }) .collect() } ``` Я подозреваю, что `calculate_discount` возвращает неправильное значение, но не могу понять, что именно идёт не так. `println!` выводит кучу мусора в терминал, а `dbg!` захламляет вывод и не даёт нормально посмотреть на состояние на каждом шаге итерации. Мне нужен именно пошаговый отладчик: поставить breakpoint, посмотреть переменные, пройти в функцию, увидеть стек вызовов. Как это сделать в Rust? Я слышал про LLDB, GDB и VS Code, но не понимаю, что выбрать и как настроить. Пожалуйста, объясните как для новичка: какой отладчик использовать, как его установить и как с ним работать шаг за шагом. И можно ли дебажить код прямо в терминале, без IDE? Заранее спасибо! <<>>

Выбор отладчика для Rust

Для Rust доступны два основных подхода к пошаговой отладке:

  • Терминальные отладчики — GDB (GNU Debugger) и LLDB (LLVM Debugger). Работают из командной строки.
  • Интегрированные отладчики — встроенные в VS Code, IntelliJ Rust, CLion. Наиболее удобны для новичков.

Я рекомендую начать с VS Code + CodeLLDB — это самый простой и стабильный вариант. Но если вы фанат терминала, покажу и GDB.

Способ 1: VS Code + CodeLLDB (рекомендую)

Шаг 1. Установите расширение
Откройте VS Code, перейдите во вкладку Extensions (Ctrl+Shift+X) и установите CodeLLDB от Vadim Chugunov.

Шаг 2. Настройте конфигурацию запуска
Создайте файл .vscode/launch.json в корне проекта. Вставьте:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug Rust",
            "cargo": {
                "args": ["build", "--bin", "название_вашего_бина"]
            },
            "args": [],
            "cwd": "${workspaceFolder}"
        }
    ]
}

Шаг 3. Поставьте точку останова
Кликните слева от номера строки в редакторе — появится красный кружок. Например, поставьте его на строке let discount = calculate_discount(&o);.

Шаг 4. Запустите отладку
Нажмите F5. VS Code соберёт проект в debug-режиме и остановится на точке останова. Теперь вы можете:

  • Нажать F10 — шаг с обходом (не заходит в функцию)
  • Нажать F11 — шаг с заходом (заходит в calculate_discount)
  • Нажать Shift+F11 — выход из текущей функции
  • Смотреть переменные в левой панели "VARIABLES"
  • Наводить мышь на переменные в коде — всплывает значение

Способ 2: Терминальный отладчик GDB

Если вы предпочитаете командную строку:

Шаг 1. Установите GDB
Linux: sudo apt install gdb
macOS: brew install gdb (потребуется подписать сертификат)
Windows: установите через MSYS2 или WSL.

Шаг 2. Соберите с отладочной информацией
cargo build по умолчанию собирает debug-версию. Если нет — добавьте в Cargo.toml:

[profile.dev]
debug = true

Шаг 3. Запустите отладку
gdb target/debug/ваша_программа

Внутри GDB:

  • break process_orders — точка останова на функции
  • run — запуск программы
  • next (или n) — шаг с обходом
  • step (или s) — шаг с заходом
  • print o — вывести значение переменной
  • backtrace (или bt) — стек вызовов
  • quit — выход

Советы для вашего конкретного случая

В вашем коде с process_orders цепочка итераторов — отладчик покажет каждую итерацию. Поставьте breakpoint внутри .map() и смотрите o на каждом шаге. Чтобы увидеть discount, зайдите в calculate_discount через F11.

Важно: Для блочных отладчиков в VS Code вы можете добавить watch expression — например, o.amount - discount — и смотреть, как меняется значение на каждой итерации.

Надеюсь, теперь отладка Rust перестанет быть для вас "слепым котёнком"!

<<>>

Альтернатива: отладка через Rust Analyzer + breakpoints в коде

Если вы не хотите настраивать полноценный отладчик или работаете на удалённом сервере без GUI, есть лёгкий способ — использовать макрос std::dbg! в сочетании с eprintln! и условной компиляцией.

Но есть и более элегантный метод — библиотека inventory или самописный "отладчик" на основе макросов. Например:

macro_rules! debug_step {
    ($($arg:tt)*) => {
        #[cfg(debug_assertions)]
        {
            eprintln!("[DEBUG] {}", format!($($arg)*));
            // Имитация остановки: ждём Enter
            let mut input = String::new();
            std::io::stdin().read_line(&mut input).unwrap();
        }
    };
}

// Использование:
fn process_orders(orders: Vec<Order>) -> Vec<ProcessedOrder> {
    orders.into_iter().filter(|o| {
        debug_step!("Фильтр: order id={}, status={:?}", o.id, o.status);
        o.status == OrderStatus::Pending
    }).map(|o| {
        debug_step!("Обработка order id={}", o.id);
        // ...
    }).collect()
}

Этот подход работает только в debug-режиме (без флага --release). Вы видите пошаговый вывод и нажимаете Enter, чтобы продолжить. Минус — нет просмотра стека вызовов, но для быстрой диагностики это спасает.

Для более продвинутого терминального опыта попробуйте rr (Record and Replay) от Mozilla — он записывает выполнение и позволяет шагать назад. Установка: cargo install rr. Запуск: rr record target/debug/программа, затем rr replay.