RustMiddleExperience
Для каких задач Rust является сильным выбором, а где лучше честно выбрать другой язык?
Rust оправдан для системного ПО, высоконагруженных сервисов и WebAssembly, где важны предсказуемая latency и memory safety. Для CRUD, прототипов и команд без опыта Rust лучше выбрать Go или Python.
Когда Rust — правильный выбор
Rust оправдывает затраты на крутую кривую обучения в нескольких конкретных сценариях:
- Системное программирование и embedded — драйверы, прошивки, операционные системы. Нет рантайма и GC:
no_std-крейты работают на микроконтроллерах с 64 КБ RAM. - Высоконагруженные сетевые сервисы — HTTP-прокси, DNS-резолверы, брокеры сообщений. Tokio + Hyper дают latency p99 < 1 мс без stop-the-world пауз.
- WebAssembly — Rust компилируется в WASM лучше, чем большинство языков: размер бинаря минимальный, wasm-bindgen делает интеграцию с JS прозрачной.
- Безопасность-критичные парсеры и криптография — borrow checker исключает use-after-free и data races на этапе компиляции, что критично для кода, обрабатывающего недоверенный ввод.
- CLI-инструменты — быстрый старт, один статический бинарь, кросс-компиляция через
cross.
Когда лучше выбрать другой язык
- CRUD-приложения и внутренние сервисы — Go или Python/FastAPI дадут тот же throughput при в 3–5 раз меньших затратах на разработку. Borrow checker замедляет итерации.
- Маленькая команда без опыта Rust — onboarding занимает 2–6 месяцев до продуктивности. Это реальная стоимость.
- Data science и ML-пайплайны — экосистема уступает Python: нет аналога pandas/sklearn/torch с таким же community.
- Прототипирование — borrow checker мешает быстро менять архитектуру. Python или Go позволяют проверить гипотезу за день.
- Легаси-интеграции — если большая часть кода уже на JVM или .NET, добавление Rust создаёт FFI-мост с собственной сложностью.
Практический пример выбора
// Rust оправдан: парсер сетевых пакетов с нулевым копированием
use nom::{bytes::complete::take, number::complete::be_u16, IResult};
#[derive(Debug)]
struct EthernetFrame<'a> {
dst_mac: &'a [u8],
src_mac: &'a [u8],
ethertype: u16,
payload: &'a [u8],
}
fn parse_frame(input: &[u8]) -> IResult<&[u8], EthernetFrame> {
let (input, dst_mac) = take(6usize)(input)?;
let (input, src_mac) = take(6usize)(input)?;
let (input, ethertype) = be_u16(input)?;
Ok((&[][..], EthernetFrame { dst_mac, src_mac, ethertype, payload: input }))
}
// Lifetime 'a гарантирует: payload живёт не дольше входного буфера
// Никакого копирования — zero-copy parsing прямо из сетевого буфера
Подводные камни
- Недооценка времени на обучение: «пишем на Rust» без опытного ревьюера в команде ведёт к клонированию всего подряд вместо понимания borrow checker.
- Async Rust сложнее синхронного в 3–4 раза: futures, pinning, Send-bounds — это отдельная область знаний поверх базового Rust.
- Ecosystem maturity неравномерна: для HTTP всё отлично (axum, actix), для GUI или ORM — значительно слабее чем в Go или Java.
- Compile time: большой проект на Rust компилируется 2–10 минут даже с incremental builds. Это реально замедляет CI.
- Cargo.lock не решает проблему supply chain: транзитивные зависимости могут тянуть небезопасный unsafe-код из чужих крейтов.
- unsafe блоки снимают гарантии компилятора — их наличие в кодовой базе требует дополнительного аудита, как и в C.
- Нет стабильного ABI: Rust-библиотеки нельзя просто так шарить между крейтами разных версий компилятора без
extern "C". - Для команды с Go-бэкграундом переключение на Rust не всегда даёт выигрыш: Go тоже очень быстрый, а код проще поддерживать.
What hurts your answer
- Выбирать Rust по популярности, а не по требованиям проекта
- Игнорировать опыт команды, эксплуатацию и стоимость поддержки
- Не называть ситуации, где Rust будет плохим выбором
What they're listening for
- Называет критерии выбора Rust
- Учитывает команду, эксплуатацию, стоимость и риски
- Может назвать сценарии, где выбрал бы альтернативу