Rust
This document outlines the coding standards and best practices for Rust development in our projects.
Naming Conventions
Types and Traits
struct UserAccount {
username: String,
email: String,
}
trait Processable {
fn process(&self) -> Result<(), Error>;
}
enum Status {
Active,
Inactive,
Suspended,
}
Functions and Variables
Use snake_case for functions, methods, and variables
Use SCREAMING_SNAKE_CASE for constants
fn calculate_total(items: &[Item]) -> f64 {
let mut total_price = 0.0;
// Implementation
total_price
}
const MAX_CONNECTIONS: usize = 100;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
Error Handling
Use Result Type
Always use Result<T, E> for operations that can fail. Never unwrap unless safety is absolutely guaranteed.
// BAD
struct Config {
path: Option<String>
}
fn get_path_from_cfg(cfg: &Config) -> Result<String> {
Ok(cfg.path.unwrap()) // runtime panic if cfg.path is None
}
// GOOD
struct Config {
path: Option<String>
}
fn get_path_from_cfg(cfg: &Config) -> Result<String> {
if let Some(path) = cfg.path {
return Ok(path);
}
bail!("Missing path in config")
}
Custom Error Types
Define custom error types for your domain.
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataError {
#[error("Invalid data format: {0}")]
InvalidFormat(String),
#[error("Data not found")]
NotFound,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
Ownership and Borrowing
Prefer Borrowing
Use references to avoid unnecessary cloning.
// Good - uses borrowing
fn process_data(data: &Vec<String>) {
for item in data {
println!("{}", item);
}
}
// Avoid - takes ownership
fn process_data_bad(data: Vec<String>) {
// data is consumed
}
Use Appropriate Mutability
Only use mutable references when necessary.
fn read_only(data: &Data) {
// Can only read
}
fn modify(data: &mut Data) {
data.value = 42;
}
Idiomatic Rust
Use Iterator Methods
Leverage iterator combinators instead of loops when appropriate.
// Good - functional style
let sum: i32 = numbers
.iter()
.filter(|&&x| x > 0)
.map(|&x| x * 2)
.sum();
// Acceptable for complex logic
let mut sum = 0;
for num in &numbers {
if *num > 0 {
sum += num * 2;
}
}
Pattern Matching
Use pattern matching extensively.
match result {
Ok(value) => println!("Success: {}", value),
Err(e) => eprintln!("Error: {}", e),
}
// Use if let for single pattern
if let Some(value) = optional {
println!("Got: {}", value);
}
Use ? Operator
Use the ? operator for error propagation.
fn process_file(path: &str) -> Result<(), Box<dyn std::error::Error>>{
let content = read_file_content(path)?;
let parsed = parse_content(&content)?;
save_result(&parsed)?;
Ok(())
}
Documentation
Document public APIs with /// comments.
/// Calculates the fibonacci number at position `n`.
///
/// # Arguments
///
/// * `n` - The position in the fibonacci sequence
///
/// # Examples
///
/// ```
/// let result = fibonacci(10);
/// assert_eq!(result, 55);
/// ```
///
/// # Panics
///
/// Panics if `n` is greater than 186 due to overflow.
pub fn fibonacci(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
Testing
Write Unit Tests
Include unit tests in the same file using a tests module.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_total() {
let items = vec![1, 2, 3, 4, 5];
assert_eq!(calculate_sum(&items), 15);
}
#[test]
#[should_panic(expected = "division by zero")]
fn test_divide_by_zero() {
divide(10, 0);
}
}
Best Practices
Run cargo clippy - Use Clippy to catch common mistakes
Run cargo fmt - Format code consistently
Avoid unwrap() and expect() in production code - Handle errors properly
Use derive macros - Automatically implement common traits
Keep functions small - Each function should have a single responsibility
Use const and static appropriately - For compile-time constants
Leverage the type system - Use newtype pattern to prevent bugs
Code Organization
// Import order
use std::collections::HashMap;
use std::fs::File;
use serde::{Deserialize, Serialize};
use tokio::runtime::Runtime;
use crate::models::User;
use crate::utils::helpers;
// Module structure
pub mod models;
pub mod services;
pub mod utils;
// Re-exports
pub use models::User;
pub use services::DataService;
Use &str instead of String when you don't need ownership
Use Vec::with_capacity() when you know the size
Use clone() judiciously - consider borrowing or Rc/Arc
Profile before optimizing - don't guess
Consider using Cow<str> for string handling flexibility
Last modified: 11 December 2025