askill
rust-best-practices

rust-best-practicesSafety 95Repository

Rust development best practices, patterns, and conventions. Use when writing Rust code, reviewing .rs files, discussing ownership, lifetimes, borrowing, or Cargo configuration. Triggers on mentions of Rust, Cargo, ownership, borrowing, lifetimes, traits, async Rust, tokio.

0 stars
1.2k downloads
Updated 12/9/2025

Package Files

Loading files...
SKILL.md

Rust Best Practices

Ownership and Borrowing

Prefer Borrowing Over Ownership

// Bad - takes ownership unnecessarily
fn print_name(name: String) {
    println!("{}", name);
}

// Good - borrows immutably
fn print_name(name: &str) {
    println!("{}", name);
}

Use References Appropriately

// Immutable borrow for reading
fn calculate_length(s: &String) -> usize {
    s.len()
}

// Mutable borrow for modification
fn push_char(s: &mut String, c: char) {
    s.push(c);
}

Error Handling

Use Result and Option

fn find_user(id: u32) -> Option<User> {
    users.get(&id).cloned()
}

fn parse_config(path: &str) -> Result<Config, ConfigError> {
    let content = fs::read_to_string(path)?;
    toml::from_str(&content).map_err(ConfigError::Parse)
}

Custom Error Types

use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("not found: {0}")]
    NotFound(String),

    #[error("validation error: {field}")]
    Validation { field: String },
}

Propagate with ?

fn process_file(path: &str) -> Result<Data, Error> {
    let content = fs::read_to_string(path)?;
    let parsed = serde_json::from_str(&content)?;
    let validated = validate(parsed)?;
    Ok(validated)
}

Structs and Enums

Builder Pattern

#[derive(Default)]
pub struct ServerBuilder {
    port: Option<u16>,
    host: Option<String>,
}

impl ServerBuilder {
    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    pub fn host(mut self, host: impl Into<String>) -> Self {
        self.host = Some(host.into());
        self
    }

    pub fn build(self) -> Result<Server, BuildError> {
        Ok(Server {
            port: self.port.unwrap_or(8080),
            host: self.host.unwrap_or_else(|| "localhost".into()),
        })
    }
}

Newtype Pattern

pub struct UserId(u64);
pub struct Email(String);

impl Email {
    pub fn new(email: String) -> Result<Self, ValidationError> {
        if email.contains('@') {
            Ok(Self(email))
        } else {
            Err(ValidationError::InvalidEmail)
        }
    }
}

Enums for State

enum ConnectionState {
    Disconnected,
    Connecting { attempt: u32 },
    Connected { session_id: String },
    Error { message: String },
}

Traits

Implement Standard Traits

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct User {
    pub id: u64,
    pub name: String,
}

Trait Objects vs Generics

// Static dispatch (monomorphization)
fn process<T: Handler>(handler: T) { ... }

// Dynamic dispatch (trait object)
fn process(handler: &dyn Handler) { ... }
fn process(handler: Box<dyn Handler>) { ... }

Extension Traits

pub trait StringExt {
    fn truncate_ellipsis(&self, max_len: usize) -> String;
}

impl StringExt for str {
    fn truncate_ellipsis(&self, max_len: usize) -> String {
        if self.len() <= max_len {
            self.to_string()
        } else {
            format!("{}...", &self[..max_len - 3])
        }
    }
}

Lifetimes

Elision Rules

// Lifetimes elided - single input reference
fn first_word(s: &str) -> &str { ... }

// Explicit when multiple inputs
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... }

Struct Lifetimes

struct Parser<'a> {
    input: &'a str,
    position: usize,
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Self {
        Self { input, position: 0 }
    }
}

Async Rust

Tokio Patterns

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result = fetch_data().await?;
    Ok(())
}

async fn fetch_data() -> Result<Data, Error> {
    let client = reqwest::Client::new();
    let response = client.get(URL).send().await?;
    response.json().await.map_err(Into::into)
}

Concurrent Operations

use futures::future::join_all;

async fn fetch_all(urls: Vec<String>) -> Vec<Result<Response, Error>> {
    let futures: Vec<_> = urls.into_iter()
        .map(|url| fetch_one(url))
        .collect();

    join_all(futures).await
}

Testing

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    #[should_panic(expected = "division by zero")]
    fn test_divide_by_zero() {
        divide(1, 0);
    }
}

Integration Tests

// tests/integration_test.rs
use mylib::process;

#[test]
fn test_full_workflow() {
    let result = process("input");
    assert!(result.is_ok());
}

Performance

Avoid Unnecessary Allocations

// Bad - allocates new String
fn process(s: &str) -> String {
    s.to_uppercase()
}

// Good when possible - use Cow
use std::borrow::Cow;
fn process(s: &str) -> Cow<str> {
    if s.chars().any(|c| c.is_lowercase()) {
        Cow::Owned(s.to_uppercase())
    } else {
        Cow::Borrowed(s)
    }
}

Use Iterators

// Good - lazy, zero-cost
let sum: i32 = numbers.iter()
    .filter(|n| **n > 0)
    .map(|n| n * 2)
    .sum();

Anti-Patterns to Avoid

  • .unwrap() in production code
  • Excessive .clone() to "fix" borrow checker
  • unsafe without clear justification
  • Ignoring compiler warnings
  • Not using clippy
  • Global mutable state (lazy_static with Mutex)

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

82/100Analyzed 2/24/2026

Comprehensive Rust best practices guide covering ownership, borrowing, error handling, traits, lifetimes, async Rust, testing, and performance. Well-structured with clear code examples showing good vs bad patterns. Includes when-to-use triggers and relevant tags. Slightly incomplete - missing advanced topics like concurrency primitives, macros, and Cargo workspace configuration. The tags (ci-cd, database, testing) are somewhat misaligned with the content focus on core language patterns.

95
85
90
70
85

Metadata

Licenseunknown
Version-
Updated12/9/2025
Publishereous

Tags

ci-cddatabasetesting