Переход с Go на Rust — не про скорость и не про типы: Go и так быстр, и типы у него есть. Вопрос в гарантиях корректности, контроле над рантаймом и удобстве для разработчика. Статья ориентирована на backend — именно там Go силён и откуда чаще всего приходят команды, думающие о Rust.
Главный сдвиг: проверки переезжают в систему типов. Nil, ошибки, data races, ресурсы, отмена — в Go за это отвечают конвенции, линтеры и runtime-детекторы. В Rust это типы, которые компилятор проверяет намертво. Option<T> заставляет обработать отсутствие значения — в Go забыли проверить указатель на nil, и получили панику в проде. Result<T, E> с оператором ? и эксгаустивным match заменяет if err != nil — пропустить ошибку невозможно. Send/Sync не дают скомпилировать код с гонкой данных, а Mutex<T> — единственный способ добраться до данных под блокировкой.
Go-разработчики приходят в Rust не из-за скорости, а из-за усталости от многословного error handling, уязвимости к nil-указателям, долгого отсутствия дженериков и ограничений интерфейсов. Дженерики в Go появились поздно и выглядят прикрученными — стандартная библиотека их почти не использует, а any и reflect никуда не делись. В Rust дженерики с трейтами — фундамент: Option, Result, Vec, HashMap, все коллекции generic-ные с первого дня. Мономорфизация даёт нулевую стоимость абстракции, но расплачиваетесь временем компиляции.
Concurrency — единственное, где Go реально блестит: нет раскраски функций, любой код можно запустить в горутине без изменения сигнатуры. В Rust async/await требует явного выбора экзекьютора (обычно tokio), Send/Sync отслеживаются на .await-границах. Отмена операций — через CancellationToken вместо context.Context, но компилятор может проверить, что вы его не забыли.
Инструментарий: cargo покрывает больше задач из коробки, чем Go toolchain (линтер clippy, профилировщик, аудит уязвимостей), хотя в Go сторонние инструменты вроде golangci-lint тоже хорошо работают. Оба сообщества сошлись на ценности единого форматтера (gofmt/rustfmt) — пусть неидеального, но устраняющего споры о стиле.
Для типичного backend-стека в Rust устоялись axum + sqlx + tokio + tracing. Миграция с Go — это не революция, а постепенный перенос: маппинг паттернов, перевод nil в Option, if err != nil в ?, горутин в tokio::spawn. Rust требует больше усилий на старте, но компилятор ловит целые категории багов, которые в Go находят только в продакшне.