Code review в Rust: что проверять и как давать обратную связь на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему code review в Rust — это не просто формальность

Code review — один из ключевых процессов в разработке любого качественного ПО. Но когда речь заходит о Rust, этот процесс приобретает особое значение. Rust — язык с уникальной системой владения (ownership) и заимствования (borrowing), который компилятор проверяет на этапе компиляции. Однако даже если код компилируется, это не значит, что он написан хорошо, безопасно и эффективно.

В этой статье мы разберём, на что именно обращать внимание при code review Rust-проектов, как давать конструктивную обратную связь, чтобы не обидеть коллегу, и как избежать типичных ошибок, которые не ловит компилятор. Статья ориентирована на разработчиков, которые уже знакомы с основами Rust, но хотят углубить свои навыки ревью.

1. Безопасность памяти и управление ресурсами

Rust гарантирует безопасность памяти без сборщика мусора. Но это не отменяет необходимости проверять, как разработчик использует unsafe блоки, сырые указатели и внешние FFI-вызовы.

Что проверять:

  • Использование unsafe: каждый unsafe блок должен быть оправдан и снабжён комментарием, почему он безопасен. Если можно обойтись без unsafe — лучше так и сделать.
  • Утечки ресурсов: проверяйте, что все Box, Rc, Arc и другие умные указатели корректно освобождаются. Например, циклические ссылки через Rc без Weak — частая проблема.
  • Drop-порядок: если структура содержит поля с кастомной реализацией Drop, убедитесь, что порядок уничтожения не приведёт к ошибкам.
// Пример плохого кода: unsafe без комментариев
unsafe {
    let ptr = ...;
    // Нет объяснения, почему это безопасно
    *ptr = 42;
}

// Хороший код: комментарий и безопасная альтернатива
// Используем unsafe только для вызова C-функции, которая гарантирует валидность указателя
unsafe {
    // Указатель получен из C API, который гарантирует его валидность в течение жизни вызова
    some_c_function(ptr);
}

2. Идиоматичность кода и стиль

Rust имеет свои идиомы, которые отличаются от C++ или Python. Code review должен проверять, насколько код соответствует духу языка.

Что проверять:

  • Использование Option и Result: избегайте unwrap() и expect() без явной причины. В production-коде используйте обработку ошибок через match, ? оператор или unwrap_or_else.
  • Pattern matching: предпочитайте if let и match вместо цепочек is_some()/is_ok().
  • Использование трейтов: проверяйте, что трейты реализованы осмысленно. Например, реализация Display должна давать читаемый вывод, а не просто Debug.
  • Именование: в Rust принято использовать snake_case для переменных и функций, CamelCase для типов и трейтов. Константы — SCREAMING_SNAKE_CASE.
// Плохо: избыточное использование unwrap
let value = some_option.unwrap();

// Хорошо: обработка ошибок
let value = match some_option {
    Some(v) => v,
    None => return Err("Value not found".into()),
};

// Ещё лучше: с оператором ?
let value = some_option.ok_or("Value not found")?;

3. Производительность и аллокации

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

Что проверять:

  • Лишние аллокации: проверяйте, не создаётся ли String или Vec там, где можно использовать слайсы (&str, &[T]).
  • Клонирование: clone() — частый источник лишних копий. Если тип реализует Copy, это не страшно, но для больших структур лучше передавать по ссылке.
  • Итераторы vs циклы: итераторы в Rust часто так же быстры, как ручные циклы, но код с ними чище. Однако проверяйте, что цепочки итераторов не создают лишних промежуточных коллекций (например, .collect() без необходимости).
  • Блокировки: если используются Mutex или RwLock, проверьте, что блокировка удерживается минимальное время.
// Плохо: лишнее клонирование
fn process(data: &Vec<String>) {
    let cloned = data.clone(); // копируем весь вектор
    for item in cloned {
        // ...
    }
}

// Хорошо: работаем по ссылке
fn process(data: &[String]) {
    for item in data {
        // ...
    }
}

4. Как давать обратную связь: конструктивно и без обид

Code review — это не только техническая проверка, но и коммуникация. В Rust-сообществе принято быть вежливым и помогать коллегам расти.

Принципы хорошей обратной связи: