Добавить объявление

Типичные ошибки новичков в Rust и как их исправить

Наткнулся на интересную статью, которая разбирает по косточкам код начинающего Rust-разработчика. В ней автор не просто критикует, а проводит живой рефакторинг: шаг за шагом превращает работающий, но странный код в чистый Rust. Это не скучная теория, а практический разбор, как если бы опытный коллега сел рядом и начал объяснять: «Вот тут можно лучше, а здесь совсем по-другому».

Особенно зацепило, что разбор идет от типичных ошибок новичков:


  • Множественное повторение одно и того же кода.

  • Возврат кортежей (Student, bool) вместо понятного Result.


  • // Было: что означает этот bool? true - ок? false - ошибка?
    fn add_student() -> (Student, bool) { ... }

    // Стало: ясная семантика успеха или ошибки
    fn add_student() -> Result { ... }


      Странные циклы с ручным управлением счетчиком.

// Было: зачем нам mutable счетчик тут?
let mut i: i8 = 1;
loop { i += 1; ... }

// Стало: чисто и понятно
for i in 1usize.. { ... }


Кроме того, в статье есть решения, которые не только делают код лучше, но и учат думать как опытный Rust-разработчик, правильно использовать систему типов, инкапсуляцию через модули и писать тестируемый код с первого раза.

Ключевые шаги рефакторинга:


  • Инкапсуляция данных: вместо голого Vec завели структуру StudentDatabase. Это не просто обертка - это способ сказать: «Вот ваш интерфейс для работы со студентами, а внутреннее устройство - моя забота».


  • // Было: прямой доступ к вектору, можно сделать что угодно
    let mut db: Vec = Vec::new();
    db.push(student);

    // Стало: данные защищены, интерфейс четкий
    let mut db = StudentDatabase::new();
    db.add(student);

  • Юнит-тестирование как дизайн-инструмент: хотите написать тестируемый код? Спросите себя: «Как я буду это тестировать?». Это заставит вас делать правильные абстракции. Например, чтобы протестировать вывод в консоль, пришлось изменить метод display(). Вместо прямого вызова println!() он теперь принимает любой объект, реализующий std::io::Write.


  • // Старая версия, которую не протестировать
    pub fn display(&self) {
    for student in &self.db {
    println!("{}", student);
    }
    }

    // Новая версия: тестируема и гибка
    pub fn display_on(&self, output: &mut impl std::io::Write) -> io::Result<()> {
    writeln!(output, "Студенты:")?;
    for student in &self.db {
    write!(output, " - {}\n", student)?;
    }
    Ok(())
    }

    // В продакшене: вывод в консоль
    db.display_on(&mut std::io::stdout())?;

    // В тесте: вывод в буфер
    let mut buffer = Vec::new();
    db.display_on(&mut buffer)?;
    assert!(buffer.len() > 0);

  • Использование силы системы типов: Rust не ООП-язык. Его сила в системе типов и трейтах. Вместо того чтобы создавать глубокие иерархии классов, думайте о том, что может делать ваша структура (через трейты) и какие данные она хранит.

Вывод:


Статья отлично показывает, что рефакторинг - это не про «сделать красиво», а про то, чтобы код стал предсказуемым, поддерживаемым и самодокументируемым.
21.12.2025 9 225