Clean Code in Rust: Principles and Practical Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Clean Code in Rust: Principles and Practical Examples



In the world of software development, writing working code is only half the battle. True mastery lies in the ability to create code that is easy to read, maintain, and extend. For the Rust language, known for its memory safety and performance, Clean Code principles are especially important. In this article, we will break down the key principles of writing clean code in Rust and support them with practical examples.



Why is Clean Code Important for Rust?



Rust is a powerful language with a unique system of ownership and borrowing. Despite its strictness, it is easy to create confusing code that is difficult to maintain. Clean Code helps to:



  • Reduce the number of errors at compile time and runtime.
  • Simplify teamwork and code reviews.
  • Increase development productivity through readability.
  • Make code self-documenting.


Principle 1: Naming Variables and Functions



A good name is the foundation of clean code. In Rust, where types are strict, this is especially important. Avoid single-letter variables (except for loop indices) and use snake_case for variables and functions.



Bad example:

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


Good example (Clean Code):

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


Recommendations:

  • Use verbs for functions: process_data(), validate_input().
  • Use nouns for variables: user_count, error_message.
  • Avoid abbreviations: calc → calculate, tmp → temporary_value.


Principle 2: Small Functions and Single Responsibility



Each function should do only one thing. In Rust, this is especially helpful when working with closures and iterators.



Bad example (function does too much):

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}


Good example (split into small functions):

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()}


Note: in the second example, we used iterators, which makes the code more idiomatic for Rust.



Principle 3: Error Handling and Option/Result



Rust offers powerful Option and Result types for error handling. Clean code requires explicit handling of all possible cases.



Bad example (ignoring errors):

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


Good example (clean error handling):

fn divide(a: f64, b: f64) -> Result<f64, String> {    if b == 0.0 {        Err("Division by zero                

Blogs

Book Recommendations