Clean Code на Rust: принципы и примеры для начинающих

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

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

Начать курс

Clean Code на Rust: принципы и примеры для начинающих

Rust — мощный системный язык программирования, который славится своей производительностью и безопасностью памяти. Однако, как и в любом другом языке, написание чистого кода (Clean Code) является критически важным навыком. Чистый код легче читать, поддерживать и расширять. Для начинающих разработчиков на Rust освоение принципов Clean Code с самого начала поможет избежать типичных ошибок и сделать ваш код элегантным и надёжным. В этой статье мы разберём основные принципы чистого кода на Rust с конкретными примерами.

1. Именование переменных и функций

Хорошее имя должно объяснять намерение программиста. В Rust принято использовать snake_case для переменных, функций и модулей, и CamelCase для типов и трейтов.

Плохой пример:

fn c(a: i32, b: i32) -> i32 {
    let x = a + b;
    x
}

Хороший пример:

fn calculate_sum(first_number: i32, second_number: i32) -> i32 {
    let result = first_number + second_number;
    result
}

Используйте осмысленные имена: user_name вместо un, process_data вместо pd. Избегайте сокращений, если они не общеприняты (например, idx для индекса).

2. Маленькие функции и единая ответственность

Принцип единственной ответственности (Single Responsibility Principle) гласит: каждая функция должна делать только одну вещь. Если функция выполняет несколько действий, её сложно тестировать и изменять.

Плохой пример (функция делает слишком много):

fn process_user(user_id: u32) {
    // Получение пользователя из БД
    // Валидация данных
    // Отправка email
    // Логирование
    println!("Processing user {}", user_id);
}

Хороший пример (разделение на маленькие функции):

fn get_user(user_id: u32) -> User {
    // Логика получения пользователя
    User { id: user_id }
}

fn validate_user(user: &User) -> bool {
    // Логика валидации
    user.id > 0
}

fn send_email(user: &User) {
    // Логика отправки email
    println!("Email sent to user {}", user.id);
}

fn process_user(user_id: u32) {
    let user = get_user(user_id);
    if validate_user(&user) {
        send_email(&user);
    }
}

Старайтесь, чтобы функция умещалась в 10-20 строк. Если она длиннее — разбейте её.

3. Работа с ошибками: используйте Result и Option

Rust поощряет явную обработку ошибок через типы Result<T, E> и Option<T>. Избегайте использования .unwrap() или .expect() в production-коде — это приводит к панике при ошибке.

Плохой пример (опасное извлечение):

fn read_file(path: &str) -> String {
    let content = std::fs::read_to_string(path).unwrap(); // Может упасть!
    content
}

Хороший пример (обработка ошибки):

use std::fs;

fn read_file(path: &str) -> Result<String, std::io::Error> {
    let content = fs::read_to_string(path)?; // Оператор ? для проброса ошибки
    Ok(content)
}

fn main() {
    match read_file("data.txt") {
        Ok(data) => println!("File content: {}", data),
        Err(e) => eprintln!("Error reading file: {}", e),
    }
}

Используйте ? оператор для краткости, но всегда обрабатывайте ошибки на верхнем уровне. Для собственных ошибок создавайте перечисления с трейтом std::error::Error.

4. Комментарии и документация

Чистый код должен быть самодокументируемым. Комментарии объясняют почему, а не что. Для документирования публичных API используйте /// (doc comments).

Плохой пример (бесполезный комментарий):

// Увеличиваем счётчик на 1
counter += 1;

Хороший пример (объяснение причины):

/// Вычисляет факториал числа n.
/// Использует итеративный подход для избежания переполнения стека.
fn factorial(n: u32) -> u64 {
    (1..=n).product()
}

Избегайте закомментированного кода — используйте системы контроля версий. Если код не нужен — удалите его.

5. Избегайте магических чисел и строк

Магические числа (числа без пояснения) затрудняют понимание кода. Выносите их в константы.

Плохой пример:

fn calculate_discount(price: f64) -> f64 {
    price * 0.15 // Что такое 0.15?
}

Хороший пример:

const DISCOUNT_RATE: f64 = 0.15;

fn calculate_discount(price: f64) -> f64 {
    price * DISCOUNT_RATE
}

Константы делают код понятным и упрощают изменение значений в будущем.

6. Используйте идиоматичные конструкции Rust

Rust имеет свои идиомы, которые делают код чище. Например, используйте if let и while let для работы с Option/Result, а также цепочки итераторов.

Пример с if let (вместо match для одного случая):

let value = Some(42);

// Вместо:
match value {
    Some(v) => println!("Found: {}", v),
    None => (),
}

// Лучше:
if let Some(v) = value {
    println!("Found: {}", v);
}

Пример с итераторами (вместо циклов):

let numbers = vec![1, 2, 3, 4, 5];

// Плохо (императивный стиль):
let mut result = Vec::new();
for n in &numbers {
    if n % 2 == 0 {
        result.push(n * 2);
    }
}

// Хорошо (функциональный стиль):
let result: Vec<i32> = numbers.iter()
    .filter(|&&n| n % 2 == 0)
    .map(|&n| n * 2)
    .collect();

Функциональный стиль часто более читаем и менее подвержен ошибкам.

Блоги

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