Миграция с GO на RUST что стоит учитывать

25.05.2026 · 5 мин

Представь: твой Go-сервис работает месяцами без проблем, а потом в 3 часа ночи прилетает алерт — segmentation fault. Кто-то не проверил указатель на nil, и вся горутина падает.

Знакомая ситуация? Мне — да. И это одна из причин, почему команды начинают смотреть в сторону Rust.

Наткнулся на гайд по миграции с Go на Rust от Matthias Endler — консультанта, который помогает командам с такими переходами. Не буду пересказывать документ дословно, а разберу ключевые идеи, которые реально стоит учитывать.

Зачем вообще думать о миграции

Главное заблуждение: разработчики едут в Rust не потому, что Go медленный. Для большинства бэкенд-задач Go достаточно быстр.

Проблемы в другом:

Автор гайда формулирует это так: в Go многое держится на конвенциях, инструментах и рантайм-детекте. В Rust это кодируется прямо в систему типов, которую проверяет компилятор.

Что даёт система типов RUST на практике

Здесь начинается самое интересное.

В Rust Option<T> — это тип, который заставляет тебя обработать случай None. Ты физически не можешь разыменовать Option без проверки. Компилятор не пропустит.

fn handle(&self, req: &Request) -> Result<(), ServiceError> {
    let user = self.repo.find(req.user_id)?; // ? означает "верни ошибку если None"
    user.notify()
}

В Go тот же код выглядит примерно так:

user, err := s.repo.Find(req.UserID)
if err != nil {
    return err
}
return user.Account.Notify() // а если Account == nil?

Разница в том, что в Go компилятор не заставляет тебя думать о None-case. В Rust — заставляет.

С гонками данных аналогично. В Rust shared mutable state между потоками можно только через типы, которые реализуют Send и Sync. Попробуй передать HashMap между потоками — программа просто не скомпилируется.

Сравнение подходов к безопасности
─────────────────────────────────
Go:  Конвенции → Инструменты → Рантайм
     ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░
     (некоторые ошибки ловятся,
      многие — только в проде)

Rust: Типы → Компилятор → Готово
     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
     (большинство ошибок
      невозможно написать)
Чем раньше ошибка обнаруживается, тем дешевле её исправить

Это не магия. Это архитектура языка. Мутабельный доступ к данным в Rust требует либо эксклюзивного владения (данные у тебя одного), либо обёртки типа Arc<Mutex<...>>. Тип делает контракт явным.

Инструментарий: где RUST и GO похожи

Хорошая новость: если ты привык к Go toolchain, Rust не станет шоком.

Cargo — это как go build + go test + go doc + go get в одном флаконе. Базовые вещи встроены, расширения ставятся одной командой и чувствуются нативно.

Экосистема инструментов
───────────────────────
┌─────────────┬────────────────────┐
│   Задача    │  Rust              │
├─────────────┼────────────────────┤
│ Сборка      │ cargo build        │
│ Тесты       │ cargo test         │
│ Форматир.   │ cargo fmt          │
│ Линтинг     │ cargo clippy       │
│ Документация│ cargo doc --open   │
│ Профилировани│ cargo flamegraph  │
│ Зависимости │ cargo add/update   │
└─────────────┴────────────────────┘
(Всё из коробки, расширения — 
 через cargo install)
В Rust инструменты первой стороны покрывают больше, чем в Go

Оба языка сошлись на важной идее: единый каноничный стиль важнее идеального стиля. gofmt и rustfmt не нравятся всем одинаково — но они устраняют споры о форматировании в код-ревью. Это стоит больше, чем любые личные предпочтения.

Как выбирать, что мигрировать первым

Автор гайда честно говорит: не начинай с критичного ядра. Это не та тема, где стоит экспериментировать.

Критерии первого модуля:

Идеальный кандидат — какой-нибудь утилитарный сервис: парсер, конвертер, воркер для фоновых задач.

Пошаговый план миграции

Вот подход, который имеет смысл:

  1. Аудит текущего кода. Пойми, что у тебя есть. Какие зависимости, какие внешние сервисы, какие контракты.
  2. Выбери пилотный модуль. По критериям выше.
  3. Параллельная реализация. Не удаляй Go-код, а пиши Rust-версию рядом.
  4. Тесты на паритет поведения. Запускай оба, сравнивай выходные данные.
  5. Поэтапное переключение трафика. Сначала 1%, потом 10%, потом 100%.
  6. Удаление старого кода. Только после уверенности в новом.

На каждом шаге нужны метрики. Время ответа, количество ошибок, использование памяти. Без данных ты не поймёшь, стало лучше или хуже.

Типичные ошибки

Стоит ли оно того?

Честный ответ: зависит от команды.

Если у тебя постоянные инциденты из-за nil-pointer errors и гонок данных — да. Если Go-сервисы работают стабильно и проблем не видно — вероятно, нет.

Rust сложнее учится. Компиляция медленнее, чем у Go. Но часть ошибок, которые раньше ловил прод, здесь ловит компилятор. Это инвестиция в надёжность.

Мне нравится мысль из гайда: в Rust проверки уходят в систему типов, и язык становится «сложнее на старте, но легче держать правильным». once you internalize that pattern, Rust stops feeling heavy and starts feeling like the compiler is doing work you used to do in your head.

Главное — не романтизировать. Оба языка работают. Выбирай тот, который решает твою проблему.

Выводы

Ссылки

Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.