Ревью кода на Rust: 5 замечаний, которые сделает каждый senior

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

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

Начать курс

Введение: Почему Rust так строг, и как code review помогает не сойти с ума

Вы написали код на Rust. Он скомпилировался — ура! Но вот беда: senior-разработчик, открывая ваш Pull Request, вздыхает и начинает писать комментарии. Знакомо? Rust — язык с уникальной философией: он доверяет вам, но проверяет всё. Однако компилятор не видит того, что видит опытный глаз. Он не скажет: «Эй, это можно сделать элегантнее» или «Твой код течёт — не памятью, так производительностью».

В этой статье мы проведём ревью кода на Rust и разберём типичные замечания senior-ов. Вы узнаете, как превратить «просто рабочий код» в идиоматичный и надёжный. Поехали!

1. Игнорирование системы типов: «А давай-ка я всё в String заверну»

Самая частая ошибка новичков — использовать String или общие типы там, где нужны конкретные. Senior скажет: «Твой тип должен отражать бизнес-логику, а не просто быть строкой».

Проблема: потеря семантики

Представьте, что вы пишете функцию для работы с email-адресами:

fn send_email(to: String, subject: String, body: String) {
    // ...
}

Что здесь не так? Вы можете перепутать to и subject — компилятор не заметит. Senior предложит использовать newtype-паттерн:

struct Email(String);
struct Subject(String);
struct Body(String);

fn send_email(to: Email, subject: Subject, body: Body) {
    // ...
}

Это кажется избыточным? На code review Rust такой подход — золотой стандарт. Он делает код самодокументируемым и исключает целый класс ошибок.

2. Злоупотребление clone(): копировать — не значит владеть

Junior-разработчики часто используют .clone() везде, где компилятор ругается на заимствования. Senior скажет: «Ты просто сдаёшься перед borrow checker-ом. Думай иначе».

Пример: лишний clone

fn process(data: Vec<i32>) {
    let first = data[0].clone();
    let second = data[1].clone();
    // ...
}

i32 реализует трейт Copy — clone() здесь абсолютно лишний. Но проблема глубже: если вы клонируете большие структуры (например, Vec или String), то получаете удар по производительности.

Правильный подход — работать с ссылками:

fn process(data: &[i32]) {
    let first = &data[0];
    let second = &data[1];
    // ...
}

На ревью кода на Rust вы обязаны объяснить, почему вы клонируете. Если ответ «чтобы компилятор не ругался» — готовьтесь к разгромному комментарию.

3. Плохая обработка ошибок: .unwrap() — это не финальное решение

Senior-ы ненавидят unwrap() и expect() в production-коде. Почему? Потому что они превращают потенциально восстанавливаемую ошибку в панику. А паника в Rust — это аварийное завершение.

Что не так с этим кодом?

let file_content = std::fs::read_to_string("config.txt").unwrap();

Если файла нет — программа упадёт. Senior предложит:

let file_content = std::fs::read_to_string("config.txt")
    .map_err(|e| format!("Failed to read config: {}", e))?;

Используйте ? оператор и собственные типы ошибок. Это основа идиоматичного Rust. На code review Rust это одно из первых замечаний.

4. Забытая производительность: аллокации и итераторы

Rust — системный язык. Senior будет смотреть не только на безопасность, но и на скорость. Одна из типичных проблем — создание лишних коллекций.

Пример: коллекционирование без нужды

let numbers: Vec<i32> = (0..100).collect();
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();

Зачем создавать Vec дважды? Можно использовать ленивые итераторы:

let doubled: Vec<i32> = (0..100).map(|x| x * 2).collect();

Или ещё лучше — передавать итератор в функцию, чтобы не аллоцировать память вообще:

fn process(iter: impl Iterator<Item = i32>) {
    for item in iter {
        // ...
    }
}

Такой подход — признак высокого уровня владения Rust. Senior обязательно отметит это на ревью кода.

5. Неправильная работа с временем жизни: «Я просто добавлю 'static»

Когда borrow checker начинает ругаться на времена жизни, многие разработчики ставят 'static как «волшебную таблетку». Senior скажет: «Ты только что запретил передавать сюда что-либо, кроме глобальных констант».

Проблема: излишний 'static

fn process(data: &'static str) {
    println!("{}", data);
}

Эта функция не примет строку, созданную на стеке. Нужно использовать обобщённое время жизни:

fn process<'a>(data: &'a str) {
    println!("{}", data);
}

Или, если время жизни не важно, используйте &str — компилятор сам выведет аннотации. На обучении Rust это одна из самых сложных тем, но senior видит эти ошибки мгновенно.

Вывод: 5 шагов к чистому коду на Rust

Итак, подведём итоги. Чтобы пройти code review Rust без красных флагов, запомните эти action items:

  • Используйте newtype-паттерн — не прячьте семантику в примитивные типы.
  • Избегайте лишних clone() — учитесь работать с ссылками и заимствованиями.
  • Не вызывайте unwrap() — обрабатывайте ошибки через Result и ?.
  • Оптимизируйте итераторы — не создавайте промежуточные коллекции без нужды.
  • Не злоупотребляйте 'static — используйте обобщённые времена жизни.

Rust — это язык, который учит думать. Каждое замечание senior-а — не критика, а шанс стать лучше. С

Блоги

Рекомендаций по книгам