Skip to content

Rust Core: Idiomatic Patterns & Philosophy

Core Philosophy

  1. Safety First: unsafe is forbidden unless the user explicitly requests it and provides a rationale. Even then, you must wrap it in a // SAFETY: comment.
  2. Expression-Oriented: Rust is an expression language. Use this.
    • Bad: let mut x = 0; if condition { x = 1; } else { x = 2; }
    • Good: let x = if condition { 1 } else { 2 };
  3. Type-Driven Design: Make invalid states unrepresentable. Use enums to encode state machines.

Idiomatic Patterns

Error Handling

  • Libraries: Use thiserror.
    #[derive(thiserror::Error, Debug)]
    pub enum MyError {
        #[error("IO failed: {0}")]
        Io(#[from] std::io::Error),
        #[error("Invalid data: {0}")]
        InvalidData(String),
    }
    
  • Applications: Use anyhow::Result.
    fn run() -> anyhow::Result<()> {
        let content = std::fs::read_to_string("config.ron")?;
        Ok(())
    }
    

Iterators vs Loops

  • Prefer Iterator combinators for transformation and filtering.
  • Bad:
    let mut results = Vec::new();
    for item in items {
        if item.is_valid() {
            results.push(item.process());
        }
    }
    
  • Good:
    let results: Vec<_> = items.iter()
        .filter(|i| i.is_valid())
        .map(|i| i.process())
        .collect();
    

Option & Result Combinators

  • Use map, and_then, unwrap_or_else.
  • Avoid excessive if let Some(x) = y nesting. - Better: let value = y.ok_or(MyError::Missing)?.process();

Project Strictness

  • Async/Await: Use tokio as the default runtime.
  • Formatting: Strictly adhere to rustfmt. Code must pass cargo fmt --check.
  • Modules: Keep main.rs small. Move logic to lib.rs or submodules (src/my_module/mod.rs or src/my_module.rs).
  • Visibility: All fields in structs are private by default. Use pub(crate) for internal sharing, pub only for API surface.