Foundational Rust style conventions that apply universally to all Rust code. Covers import organization, naming conventions, and code formatting standards...
Universal Rust style conventions for consistent, readable production code
This skill applies automatically to ALL Rust code. All other Rust skills (rust-core-patterns, axum-web-framework, rust-async-runtime, etc.) inherit these conventions.
All use imports MUST be at the top of files. Never use inline paths.
Import types and functions at the top of the file, then use the imported names in code.
// WRONG: Inline paths scattered throughout code
fn main() {
let url = std::env::var("DATABASE_URL")?;
let duration = std::time::Duration::from_secs(5);
let path = std::path::PathBuf::from("/tmp");
let pending = std::future::pending::<()>();
}
use std::env;
use std::future;
use std::path::PathBuf;
use std::time::Duration;
fn main() {
let url = env::var("DATABASE_URL")?;
let duration = Duration::from_secs(5);
let path = PathBuf::from("/tmp");
let pending = future::pending::<()>();
}
If a name is imported, USE the imported name. Never mix imported names with fully-qualified paths in the same file.
// WRONG: Import exists but fully-qualified path used anyway
use std::env::var;
fn get_config() -> String {
std::env::var("KEY").unwrap() // BAD: ignores the import
}
// CORRECT: Use the imported name
use std::env::var;
fn get_config() -> String {
var("KEY").unwrap() // GOOD: uses the import
}
This applies to ALL imports — if you write use X, then use X, never the full path.
// WRONG: Mixed usage
use std::time::Duration;
let short = Duration::from_secs(1); // Using import
let long = std::time::Duration::from_secs(60); // Ignoring import - BAD
// CORRECT: Consistent usage
use std::time::Duration;
let short = Duration::from_secs(1);
let long = Duration::from_secs(60);
Enforcement: If you see a fully-qualified path in code, check the imports. If it's already imported, use the imported name.
Group imports in this order with blank lines between groups:
// 1. Standard library
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
// 2. External crates (alphabetical)
use axum::{Router, routing::get};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
// 3. Workspace crates
use my_domain::User;
use my_utils::helpers;
// 4. Local modules
use crate::config::Config;
use crate::error::AppError;
Prefer importing the module rather than every function:
// Good: Import module, use qualified names
use std::env;
use std::fs;
let value = env::var("KEY")?;
let content = fs::read_to_string("file.txt")?;
// Also acceptable: Import specific frequently-used items
use std::time::Duration;
use std::sync::Arc;
let timeout = Duration::from_secs(30);
let shared = Arc::new(data);
Use nested imports for multiple items from the same module:
// Good: Grouped imports
use axum::{
extract::{Path, Query, State},
routing::{get, post},
Router,
};
// Avoid: Many separate use statements
use axum::extract::Path;
use axum::extract::Query;
use axum::extract::State;
use axum::routing::get;
use axum::routing::post;
use axum::Router;
Follow standard Rust naming conventions:
| Item | Convention | Example |
|---|---|---|
| Crates | snake_case | my_crate |
| Modules | snake_case | user_service |
| Types (struct, enum, trait) | PascalCase | UserService |
| Functions | snake_case | get_user |
| Methods | snake_case | find_by_id |
| Local variables | snake_case | user_count |
| Constants | SCREAMING_SNAKE_CASE | MAX_CONNECTIONS |
| Statics | SCREAMING_SNAKE_CASE | DEFAULT_TIMEOUT |
| Type parameters | PascalCase, single letter preferred | T, E, R |
| Lifetimes | lowercase, short | 'a, 'de |
All code must pass cargo fmt --check. Use default rustfmt settings unless project specifies otherwise.
// Break long chains
let result = collection
.iter()
.filter(|x| x.is_valid())
.map(|x| x.process())
.collect::<Vec<_>>();
// Break long function calls
let response = client
.post(&url)
.header("Authorization", token)
.json(&payload)
.send()
.await?;
All public items should have doc comments:
/// Creates a new user with the given email and name.
///
/// # Arguments
///
/// * `email` - The user's email address (must be valid format)
/// * `name` - The user's display name
///
/// # Errors
///
/// Returns `CreateUserError` if:
/// - Email is invalid or already exists
/// - Name is empty or too long
///
/// # Example
///
/// ```
/// let user = create_user("alice@example.com", "Alice")?;
/// ```
pub fn create_user(email: &str, name: &str) -> Result<User, CreateUserError> {
// ...
}
Add module-level documentation:
//! User management module.
//!
//! This module provides functionality for creating, updating,
//! and deleting users in the system.
mod create;
mod delete;
mod update;
Write clear, actionable error messages:
// Good: Specific and actionable
#[error("user not found with id {id}")]
NotFound { id: UserId },
#[error("email {email} is already registered")]
EmailExists { email: String },
// Bad: Vague and unhelpful
#[error("error occurred")]
GenericError,
#[error("invalid")]
Invalid,
std::time::Durationuse std::env::var; exists, use var() not std::env::var()/// doc commentsThis skill provides foundational conventions used by: