Rust Style Guide

Best practices for writing clean, readable Rust code

📝 What is Rust Style Guide?

Rust style guide provides conventions for formatting, naming, and organizing code. Following these guidelines makes your code readable, maintainable, and consistent with the Rust community standards.


// Good style - clear and consistent
fn calculate_area(width: f64, height: f64) -> f64 {
    width * height
}
                                    

Key Benefits:

✓ Improved readability
✓ Team consistency
✓ Easier maintenance

Style Guide Categories

🏷️

Naming Conventions

Consistent naming patterns

// snake_case for variables/functions
let user_name = "Alice";
fn get_user_data() { }
📐

Formatting

Code layout and spacing

// Proper indentation and spacing
if condition {
    do_something();
}
📁

Organization

File and module structure

// Clear module organization
mod utils;
mod models;
use crate::utils::*;
💬

Documentation

Comments and doc strings

/// Calculates the area of a rectangle
fn area(w: f64, h: f64) -> f64 {
    w * h
}

🔹 Naming Conventions

Rust uses specific naming patterns for different code elements:

// Variables and functions: snake_case
let user_count = 10;
let is_valid = true;
fn calculate_total() -> i32 { 42 }

// Constants: SCREAMING_SNAKE_CASE
const MAX_USERS: usize = 1000;
const PI_VALUE: f64 = 3.14159;

// Types (structs, enums): PascalCase
struct UserAccount {
    username: String,
    email: String,
}

enum PaymentStatus {
    Pending,
    Completed,
    Failed,
}

// Modules: snake_case
mod user_management;
mod payment_processing;

Naming Rules:

✓ Use descriptive names
✓ Avoid abbreviations
✓ Be consistent throughout project

🔹 Code Formatting

Proper formatting makes code easier to read and understand:

// Good formatting example
fn process_user_data(users: Vec<User>) -> Result<Vec<ProcessedUser>, Error> {
    let mut processed = Vec::new();
    
    for user in users {
        if user.is_active {
            let processed_user = ProcessedUser {
                id: user.id,
                name: user.name.trim().to_string(),
                email: user.email.to_lowercase(),
                created_at: user.created_at,
            };
            processed.push(processed_user);
        }
    }
    
    Ok(processed)
}

// Use rustfmt to automatically format your code
// Run: cargo fmt

Formatting Tips:

• Use 4 spaces for indentation
• Keep lines under 100 characters
• Add blank lines between logical sections

🔹 Documentation Style

Good documentation helps others understand your code:

/// Represents a user in the system.
/// 
/// # Examples
/// 
/// ```
/// let user = User::new("Alice", "[email protected]");
/// assert_eq!(user.name, "Alice");
/// ```
pub struct User {
    /// The user's display name
    pub name: String,
    /// The user's email address
    pub email: String,
}

impl User {
    /// Creates a new user with the given name and email.
    /// 
    /// # Arguments
    /// 
    /// * `name` - The user's display name
    /// * `email` - The user's email address
    /// 
    /// # Returns
    /// 
    /// A new `User` instance
    pub fn new(name: &str, email: &str) -> Self {
        Self {
            name: name.to_string(),
            email: email.to_string(),
        }
    }
    
    /// Checks if the user's email is valid.
    /// 
    /// Returns `true` if the email contains an '@' symbol.
    pub fn has_valid_email(&self) -> bool {
        self.email.contains('@')
    }
}

Documentation Guidelines:

• Use /// for public items
• Include examples when helpful
• Document function parameters and return values

🔹 Error Handling Style

Consistent error handling patterns improve code reliability:

use std::fs;
use std::io;

// Good error handling with Result
fn read_config_file(path: &str) -> Result<String, io::Error> {
    fs::read_to_string(path)
}

// Use ? operator for error propagation
fn load_and_parse_config(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
    let content = read_config_file(path)?;
    let config: Config = serde_json::from_str(&content)?;
    Ok(config)
}

// Handle errors appropriately
fn main() {
    match load_and_parse_config("config.json") {
        Ok(config) => {
            println!("Config loaded successfully!");
            // Use config...
        }
        Err(e) => {
            eprintln!("Failed to load config: {}", e);
            std::process::exit(1);
        }
    }
}

Error Handling Best Practices:

• Use Result for recoverable errors
• Use panic! only for unrecoverable errors
• Provide meaningful error messages

🔹 Tools for Style Consistency

Rust provides excellent tools to maintain code style:

🔧 Essential Tools:

  • rustfmt - Automatic code formatting
  • clippy - Linting and suggestions
  • cargo doc - Generate documentation
  • cargo test - Run tests and doc tests
# Format your code
cargo fmt

# Check for style issues and suggestions
cargo clippy

# Generate documentation
cargo doc --open

# Run tests including documentation examples
cargo test

🧠 Test Your Knowledge

What naming convention does Rust use for functions and variables?