Чистый код на Rust: принципы и примеры на практике

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

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

Начать курс

Clean Code на Rust: принципы и примеры на практике

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

Почему Clean Code важен для Rust?

Rust — мощный язык с уникальной системой владения (ownership) и заимствования (borrowing). Несмотря на его строгость, можно легко создать запутанный код, который будет сложно поддерживать. Clean Code помогает:

  • Уменьшить количество ошибок на этапе компиляции и выполнения.
  • Упростить командную работу и код-ревью.
  • Повысить производительность разработки за счет читаемости.
  • Сделать код самодокументируемым.

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

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

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

fn c(a: &[i32]) -> i32 {
    let mut s = 0;
    for i in a {
        s += i;
    }
    s
}

Хороший пример (Clean Code):

fn calculate_sum(numbers: &[i32]) -> i32 {
    let mut sum = 0;
    for number in numbers {
        sum += number;
    }
    sum
}

Рекомендации:

  • Используйте глаголы для функций: process_data(), validate_input().
  • Используйте существительные для переменных: user_count, error_message.
  • Избегайте сокращений: calc → calculate, tmp → temporary_value.

Принцип 2: Маленькие функции и единая ответственность

Каждая функция должна делать только одно дело. В Rust это особенно помогает при работе с замыканиями (closures) и итераторами.

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

fn process_user_data(users: &[User]) -> Vec<String> {
    let mut result = Vec::new();
    for user in users {
        if user.age > 18 {
            let name = user.name.to_uppercase();
            result.push(format!("Adult: {}", name));
        } else {
            result.push(format!("Minor: {}", user.name));
        }
    }
    result
}

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

fn classify_user(user: &User) -> &str {
    if user.age > 18 { "Adult" } else { "Minor" }
}

fn format_user_name(user: &User) -> String {
    let classification = classify_user(user);
    format!("{}: {}", classification, user.name)
}

fn process_user_data(users: &[User]) -> Vec<String> {
    users.iter().map(format_user_name).collect()
}

Обратите внимание: во втором примере мы использовали итераторы, что делает код более идиоматичным для Rust.

Принцип 3: Работа с ошибками и Option/Result

Rust предлагает мощные типы Option и Result для обработки ошибок. Чистый код требует явной обработки всех возможных случаев.

Плохой пример (игнорирование ошибок):

fn divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        panic!("Division by zero!");
    }
    a / b
}

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

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Division by zero is not allowed".to_string())
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10.0, 2.0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => eprintln!("Error: {}", e),
    }
}

Совет: Используйте оператор ? для упрощения цепочек вызовов, возвращающих Result:

fn read_and_parse(filename: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string(filename)?;
    let number = content.trim().parse::<i32>()?;
    Ok(number)
}

Принцип 4: Использование идиом Rust (итераторы, замыкания, шаблоны)

Чистый код на Rust — это код, который использует сильные стороны языка. Итераторы и замыкания часто делают код более лаконичным и безопасным.

Пример без идиом (цикл for с изменяемой переменной):

fn double_even_numbers(numbers: &[i32]) -> Vec<i32> {
    let mut result = Vec::new();
    for &num in numbers {
        if num % 2 == 0 {
            result.push(num * 2);
        }
    }
    result
}

Пример с идиомами Rust (цепочка итераторов):

fn double_even_numbers(numbers: &[i32]) -> Vec<i32> {
    numbers
        .iter()
        .filter(|&&num| num % 2 == 0)
        .map(|&num| num * 2)
        .collect()
}

Использование замыканий и шаблонов (pattern matching):

fn process_optional(value: Option<i32>) -> String {
    match value {
        Some(x) if x > 0 => format!("Positive: {}", x),
        Some(0) => "Zero".to_string(),
        Some(x) => format!("Negative: {}", x),
        None => "No value".to_string(),
    }
}

Принцип 5: Документирование и тестирование

Чистый код неотделим от

Блоги

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