← Back to Rust Course | Chapter 15: Cargo, Testing & Best Practices | Lesson 8 of 8

Writing Idiomatic Rust

Idiomatic Rust means writing code the way experienced Rust programmers naturally would, using the language's own strengths instead of fighting them.

Prefer Iterators Over Manual Loops

Idiomatic Rust often expresses transformations with iterator adapters like .map() and .filter() rather than manually written loops with mutable accumulator variables, since it is more declarative and less error-prone.

Example: Prefer Iterators Over Manual Loops

markup
// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `numbers`, set to `vec![1, 2, 3, 4, 5]`
    let numbers = vec![1, 2, 3, 4, 5];
    // Declare an immutable variable `sum_of_squares` (an integer), set to `numbers.iter().map(|n| n * n).sum()`
    let sum_of_squares: i32 = numbers.iter().map(|n| n * n).sum();
    // Print "{}" to the console, substituting in `sum_of_squares` (with a trailing newline)
    println!("{}", sum_of_squares);
}

Borrow Instead of Clone When Possible

Reaching for .clone() to sidestep a borrow-checker complaint is usually a sign the code could be restructured to borrow instead, avoiding unnecessary memory allocation and copying.

Example: Borrow Instead of Clone When Possible

markup
// Define the function `print_length` taking `s`
fn print_length(s: &str) {
    // Print "Length: {}" to the console, substituting in `s.len()` (with a trailing newline)
    println!("Length: {}", s.len());
}

// Entry point: execution of the program starts here
fn main() {
    // Declare an immutable variable `text`, set to `String::from("idiomatic")`
    let text = String::from("idiomatic");
    print_length(&text);
    // Print "Still usable: {}" to the console, substituting in `text` (with a trailing newline)
    println!("Still usable: {}", text);
}

Use the Type System to Prevent Bugs

Idiomatic Rust leans on Option, Result, and enums to make invalid states impossible to represent, catching whole categories of bugs at compile time rather than with runtime checks.

Example: Use the Type System to Prevent Bugs

markup
// Define an enum `PaymentStatus` listing its possible variants
enum PaymentStatus {
    Pending,
    Completed(f64),
    Failed(String),
}

// Define the function `describe` taking `status` and returning a String
fn describe(status: PaymentStatus) -> String {
    // Match on `status` and branch by pattern
    match status {
        PaymentStatus::Pending => String::from("still pending"),
        PaymentStatus::Completed(amount) => format!("completed for ${}", amount),
        PaymentStatus::Failed(reason) => format!("failed: {}", reason),
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Print "{}" to the console, substituting in `describe(PaymentStatus::Completed(49.99))` (with a trailing newline)
    println!("{}", describe(PaymentStatus::Completed(49.99)));
}

Listen to the Compiler

The Rust compiler's warnings, like an unused mut or an unused variable, are usually pointing at a genuine simplification opportunity, and idiomatic code takes them seriously rather than suppressing them.

Example: Listen to the Compiler

markup
fn main() {
    let value = 100; // no mut needed since it is never reassigned
    println!("Clean, warning-free code: {}", value);
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Overusing .clone() everywhere to avoid dealing with the borrow checker, instead of restructuring code to borrow properly.
  2. Writing verbose manual loops for tasks that idiomatic Rust would express with iterator adapters like .map()/.filter().
  3. Ignoring compiler warnings (like unused mut or dead code) instead of treating them as helpful hints toward cleaner code.
Chapter Summary
  • Idiomatic Rust favors iterator adapters over manual loops when they express the same logic more clearly.
  • Preferring borrowing over cloning avoids unnecessary allocations and keeps code closer to Rust's ownership philosophy.
  • Leveraging the type system (like Option/Result) to make invalid states unrepresentable is a hallmark of good Rust code.
  • Paying attention to compiler warnings often reveals opportunities to simplify or correct code.
🔒

Chapter Quiz — Complete all 8 topics to unlock

0/8 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.