RustSeniorExperience
Какие production-риски чаще всего возникают в проектах на Rust: производительность, зависимости, конкурентность, деплой или observability?
Главные Rust production-риски: неочевидные heap-аллокации в hot path, async deadlocks (await внутри MutexGuard), tokio/async-std несовместимость, .unwrap() паники и блокирующий код внутри async fn.
Production-риски в Rust-проектах
Rust устраняет целые классы runtime-ошибок, но имеет собственный набор production-рисков, многие из которых не типичны для других языков.
Производительность
- Неожиданные allocations: Box, Vec, String, Arc — аллокации не всегда очевидны. В hot path используйте arena аллокаторы (bumpalo) или stack-based структуры. Профилирование: heaptrack, DHAT.
- Monomorphization bloat: обильное использование generics приводит к раздуванию бинарника и cache misses. Решение: trait objects (dyn Trait) для неперформанс-критичных путей.
- Async runtime выбор: tokio и async-std несовместимы на уровне рантайма — смешивание в одном проекте через транзитивные зависимости вызывает панику.
Зависимости (Cargo)
- Cargo.lock не коммитится в библиотеках по умолчанию — воспроизводимость сборки нарушена. Для сервисов Cargo.lock должен быть в git.
- Supply chain атаки: cargo-audit (RustSec Advisory Database) в CI обязателен.
- Compile time: крупные зависимости (tokio, serde с derive, diesel) значительно увеличивают время сборки. Решение: sccache, cargo-chef в Docker для layer caching.
- Feature flags в зависимостях: неправильная комбинация features может включать несовместимые реализации.
Конкурентность
- Borrow checker предотвращает data races, но не deadlocks: Mutex + Mutex с нарушением порядка блокировок — классическая проблема.
- Async deadlock: .await внутри MutexGuard в tokio — блокирует executor thread.
- Arc
> везде — ложное чувство безопасности при низкой производительности. Для read-heavy workloads: RwLock или lock-free структуры (dashmap, crossbeam).
Деплой
- Бинарник статически слинкован (musl target) — отлично для контейнеров, но glibc-зависимые зависимости требуют dynamic linking.
- Размер бинарника: strip + opt-level = "z" + lto = true в release profile сокращают размер в 3–5 раз.
- Graceful shutdown: tokio::signal для SIGTERM, JoinHandle для ожидания завершения задач.
Observability
- tracing crate — стандарт для structured logging и distributed tracing в async Rust. Интеграция с OpenTelemetry через tracing-opentelemetry.
- Panic в production: по умолчанию разворачивает стек и убивает процесс. Настройте panic hook через std::panic::set_hook для логирования перед выходом.
- metrics crate + prometheus-экспортер для application метрик.
Подводные камни
- Использовать .unwrap() и .expect() в production-коде — паника при неожиданных данных убивает сервис. Обрабатывайте ошибки через anyhow/thiserror.
- Блокирующий код (std::thread::sleep, std::fs::read) внутри async fn — блокирует tokio executor. Используйте tokio::task::spawn_blocking.
- Не настраивать RUST_BACKTRACE=1 в production логах — отладка паник без backtrace крайне затруднена.
- Игнорировать clippy предупреждения в CI — многие указывают на реальные production проблемы.
- Не тестировать с sanitizers (AddressSanitizer, ThreadSanitizer) — unsafe код может содержать UB, не видимый без них.
- Слишком агрессивное использование unsafe без чёткой документации инвариантов — ошибки в unsafe аннулируют все гарантии borrow checker.
- Пропустить cargo-deny для license compliance — смешение MIT/GPL зависимостей создаёт юридические риски.
- Не профилировать compile time в CI — без sccache/cargo-chef Docker-сборка может занимать 20+ минут.
What hurts your answer
- Говорить только о запуске Rust, но не об эксплуатации
- Не упоминать observability, обновления, безопасность и rollback
- Описывать риски абстрактно, без способов их снижать
What they're listening for
- Видит production-риски Rust
- Говорит про monitoring, rollout, rollback и безопасность
- Умеет ранжировать риски по вероятности и влиянию