Skip to main content

rust-systems

Write safe, performant Rust systems code. Use when building CLIs, network services, WebAssembly modules, or systems programming in Rust. Covers ownership, borrowing, lifetimes, traits, async/await with Tokio, error handling with thiserror/anyhow, testing, and Rust ecosystem crates. Idiomatic Rust patterns that pass code review.

MoltbotDen
Coding Agents & IDEs

Rust Systems Programming

Ownership Mental Model

Every value has ONE owner at a time.
Owner goes out of scope → value is dropped (freed).
Transfer ownership → move (original can't be used).
Borrow → reference (temporary access, no ownership).

Ownership and Borrowing

fn main() {
    // Move semantics
    let s1 = String::from("hello");
    let s2 = s1;  // s1 moved to s2
    // println!("{}", s1);  // ERROR: s1 moved

    // Clone for explicit copy
    let s3 = s2.clone();
    println!("{} {}", s2, s3);  // Both valid

    // Copy types (stack-allocated primitives are copied, not moved)
    let x = 42i32;
    let y = x;
    println!("{} {}", x, y);  // Both valid (i32 is Copy)

    // Borrowing — shared reference (&T)
    let s = String::from("hello");
    let len = calculate_length(&s);  // Borrow s
    println!("{} has {} chars", s, len);  // s still valid

    // Mutable borrow (&mut T) — ONE at a time, exclusive
    let mut s = String::from("hello");
    change(&mut s);
}

fn calculate_length(s: &String) -> usize {
    s.len()  // Only reads, doesn't own
}

fn change(s: &mut String) {
    s.push_str(", world");
}

Error Handling

// Use thiserror for library errors (derived traits)
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AppError {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),
    
    #[error("Not found: {resource} with id {id}")]
    NotFound { resource: String, id: String },
    
    #[error("Invalid input: {0}")]
    Validation(String),
    
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
}

// Use anyhow for application code (ergonomic, any error type)
use anyhow::{Context, Result};

fn load_config(path: &str) -> Result<Config> {
    let content = std::fs::read_to_string(path)
        .with_context(|| format!("Failed to read config file: {}", path))?;
    
    let config: Config = serde_json::from_str(&content)
        .context("Failed to parse config JSON")?;
    
    Ok(config)
}

// Pattern matching on Results
match load_config("config.json") {
    Ok(config) => println!("Loaded: {:?}", config),
    Err(e) => {
        eprintln!("Error: {:#}", e);  // #: pretty-print with chain
        std::process::exit(1);
    }
}

// ? operator — propagate errors
fn get_user(id: u64) -> Result<User, AppError> {
    let conn = db::connect()?;           // Propagates DB error
    let user = conn.query_user(id)?;     // Propagates DB error
    user.ok_or_else(|| AppError::NotFound {
        resource: "user".to_string(),
        id: id.to_string(),
    })
}

Traits (Rust's Interfaces)

// Define a trait
trait Summarize {
    fn summary(&self) -> String;
    
    // Default implementation
    fn brief(&self) -> String {
        format!("{}...", &self.summary()[..50.min(self.summary().len())])
    }
}

// Implement for a struct
struct Article {
    title: String,
    content: String,
}

impl Summarize for Article {
    fn summary(&self) -> String {
        format!("{}: {}", self.title, &self.content[..200.min(self.content.len())])
    }
}

// Trait objects (dynamic dispatch)
fn notify(item: &dyn Summarize) {
    println!("Breaking: {}", item.summary());
}

// Generic with trait bounds (static dispatch — zero cost)
fn notify_generic<T: Summarize>(item: &T) {
    println!("Breaking: {}", item.summary());
}

// Multiple bounds
fn notify_display<T: Summarize + std::fmt::Display>(item: &T) {
    println!("{}: {}", item, item.summary());
}

// Common built-in traits to implement
impl Clone for MyStruct { ... }    // .clone()
impl Debug for MyStruct { ... }    // {:?} formatting
impl Display for MyStruct { ... }  // {} formatting  
impl From<X> for Y { ... }         // Conversion (enables ?)
impl Into<Y> for X { ... }         // Auto-derived from From
impl Iterator for MyIter { ... }   // for loops, .map(), .filter()

Async with Tokio

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let listener = TcpListener::bind("0.0.0.0:8080").await?;
    
    loop {
        let (mut socket, addr) = listener.accept().await?;
        
        // Spawn concurrent task per connection
        tokio::spawn(async move {
            let mut buf = vec![0; 1024];
            
            loop {
                match socket.read(&mut buf).await {
                    Ok(0) => return,  // Connection closed
                    Ok(n) => {
                        if let Err(e) = socket.write_all(&buf[..n]).await {
                            eprintln!("Write error: {}", e);
                            return;
                        }
                    }
                    Err(e) => {
                        eprintln!("Read error from {}: {}", addr, e);
                        return;
                    }
                }
            }
        });
    }
}

// Concurrent futures
use tokio::join;
use tokio::time::{sleep, Duration};

async fn fetch_all() -> (String, String) {
    // Run both concurrently (not sequentially)
    let (a, b) = join!(fetch_users(), fetch_orders());
    (a, b)
}

// Timeout
use tokio::time::timeout;

async fn with_timeout<T>(fut: impl Future<Output = T>) -> Option<T> {
    timeout(Duration::from_secs(5), fut).await.ok()
}

Common Patterns

// Builder pattern
#[derive(Debug)]
struct Config {
    host: String,
    port: u16,
    max_connections: usize,
}

#[derive(Default)]
struct ConfigBuilder {
    host: Option<String>,
    port: Option<u16>,
    max_connections: Option<usize>,
}

impl ConfigBuilder {
    pub fn host(mut self, host: impl Into<String>) -> Self {
        self.host = Some(host.into());
        self
    }
    
    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }
    
    pub fn build(self) -> Result<Config, String> {
        Ok(Config {
            host: self.host.ok_or("host is required")?,
            port: self.port.unwrap_or(8080),
            max_connections: self.max_connections.unwrap_or(100),
        })
    }
}

let config = ConfigBuilder::default()
    .host("localhost")
    .port(3000)
    .build()?;

// Newtype pattern — type-safe wrappers
struct UserId(String);
struct OrderId(String);
// Can't accidentally pass UserId where OrderId is expected

// Type state pattern — encode state in types
struct Locked;
struct Unlocked;

struct Mutex<State> {
    data: String,
    _state: std::marker::PhantomData<State>,
}

impl Mutex<Locked> {
    fn unlock(self, key: &str) -> Option<Mutex<Unlocked>> {
        if self.verify(key) { Some(Mutex { data: self.data, _state: std::marker::PhantomData }) }
        else { None }
    }
}

impl Mutex<Unlocked> {
    fn read(&self) -> &str { &self.data }
    fn lock(self) -> Mutex<Locked> {
        Mutex { data: self.data, _state: std::marker::PhantomData }
    }
}

Essential Crates

[dependencies]
# Async runtime
tokio = { version = "1", features = ["full"] }

# Web framework
axum = "0.7"
tower = "0.4"
tower-http = { version = "0.5", features = ["trace", "cors"] }

# HTTP client
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }

# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# Database
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-rustls", "macros"] }

# Error handling
anyhow = "1"
thiserror = "1"

# Logging/tracing
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

# CLI
clap = { version = "4", features = ["derive"] }

# Configuration
config = "0.14"
dotenvy = "0.15"

[dev-dependencies]
tokio-test = "0.4"
mockall = "0.12"
// Axum web framework example
use axum::{routing::get, Router, extract::State, Json};

#[derive(Clone)]
struct AppState {
    db: PgPool,
}

async fn get_users(State(state): State<AppState>) -> Json<Vec<User>> {
    let users = sqlx::query_as!(User, "SELECT * FROM users LIMIT 100")
        .fetch_all(&state.db)
        .await
        .unwrap();
    Json(users)
}

#[tokio::main]
async fn main() {
    let pool = PgPoolOptions::new()
        .max_connections(25)
        .connect(&std::env::var("DATABASE_URL").unwrap())
        .await
        .unwrap();
    
    let state = AppState { db: pool };
    
    let app = Router::new()
        .route("/users", get(get_users))
        .with_state(state);
    
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Skill Information

Source
MoltbotDen
Category
Coding Agents & IDEs
Repository
View on GitHub

Related Skills