How-To: Language Learning

Learning Rust for AI Agents

A condensed guide to learning Rust — specifically for the person who will be directing AI agents to write it. You don't need to be a daily Rust programmer. You need to understand enough to review, guide, and unblock AI-generated code.

May 20, 2026 ~17 min narration ~10 min read
🎤 Narration
← All How-To Guides

The Goal: Guiding Agents, Not Writing Every Line

Your job is not to become a Rust expert. Your job is to be able to:

The mental model: Think of yourself as a senior reviewer who can read code, ask good questions, and direct the builder. You don't need to lay every brick — you need to know when the walls are straight.

Rust's Design Philosophy — Why It Matters for AI Agent Output

Rust trades developer convenience for runtime safety and zero-cost concurrency. The compiler prevents entire classes of bugs before the code runs. For AI-generated code, this means the compiler is your first line of defense — if it compiles, it's generally correct.

This is actually an advantage when working with AI agents. The compiler catches the bugs that LLMs are most likely to introduce: null pointer dereferences, data races, buffer overflows, use-after-free.

The Core Trade-off

Yes — Use Rust When

High performance needed (APIs, inference runtimes, data pipelines)

Concurrency is important (async services, parallel processing)

System control matters — CPU, memory, safety guarantees

Long-lived services that need to run for months without memory leaks

FFI boundaries — Rust as a bridge between Python/ML code and systems-level operations

No — Skip Rust When

Rapid prototyping — Python for research, Rust for production

Simple scripts — bash/Python are faster for one-off tasks

Data science itself — Rust is a poor fit for exploratory analysis

Small internal tools — the compile time and learning curve aren't worth it

When you only have AI agents — Rust's strict compiler requires more human oversight than Python

The Mental Model: Ownership, Borrowing, Lifetimes

These are Rust's core concepts. They're the "weird" part that makes Rust hard. For your use case (guiding agents), you need to understand what they are and why they exist, not memorize every edge case.

1. Ownership (the single most important concept)

Every value in Rust has exactly one owner. When the owner goes out of scope, the value is dropped — deallocated immediately, no garbage collector. This is what gives Rust its memory guarantees.

// String is owned — when `s` goes out of scope, memory is freed
let s = String::from("hello");

// Transfer ownership — `s` is now invalid (move semantics)
let s2 = s;  // s cannot be used here anymore

// Copy types (i32, bool, f64, etc.) are duplicated, not moved
let x = 42;
let y = x;  // x is still valid — Copy trait

🔴 What to look for in AI-generated code: If the agent tries to use a variable after it's been "moved," the compiler will reject it. This is the #1 error patterns you'll encounter. The fix is usually: use references (&T) instead of transferring ownership.

2. Borrowing (the fix for the ownership problem)

Rust lets you create references to data without taking ownership. There are two kinds:

// Immutable borrow — many readers allowed
let ref1 = &s;
let ref2 = &s;  // Totally fine

// Mutable borrow — only ONE, and no other borrows at the same time
let mut_ref = &mut s;  // Unique mutable access — no other refs allowed

The "borrow checker" is the part of the compiler you'll argue with most. But for your role, think of it as Rust's way of asking: "Are you sure this reference won't outlive the data it points to?" — the compiler answers that for you.

3. Lifetimes (the scoping rules for references)

// The compiler tracks lifetimes automatically in most cases
// When it can't figure it out, you annotate with 'a

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}
// Compiler infers that the return value's lifetime
// is the shorter of x's and y's lifetime

🟡 Agent pitfall: AI agents sometimes struggle with lifetimes and will try to work around them with .clone() (duplicating data unnecessarily) or String::from() everywhere. Ask the agent: "Can this use references instead of cloning? What's the lifetime situation here?"

Essential Syntax You Need to Read

Data Types Quick Reference

// Primitives
let x: i32 = 42;           // signed 32-bit int
let y: u64 = 1_000_000;    // unsigned 64-bit
let z: f64 = 3.14;          // float
let b: bool = true;
let ch: char = 'A';
let s: String = String::from("heap");
let slice: &str = "stack";   // &str is a reference slice

// Collections
let mut vec: Vec<i32> = vec![1, 2, 3];
let map: HashMap<String, i32> = HashMap::new();
let set: HashSet<String> = HashSet::new();

// Enums (the powerful one)
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Common Patterns

// Match — Rust's main control flow (exhaustive!)
match result {
    Ok(value) => println!("Got: {value}"),
    Err(e) => eprintln!("Error: {e}"),
}

// If let — when you only care about one case
if let Ok(value) = result {
    println!("Success: {value}");
}

// For loop over iterators
for item in vec.iter() {   // borrows each element
    println!("{item}");
}

for item in vec.into_iter() {  // takes ownership (consumes the vec)
    println!("{item}");
}

Structs and Traits — Your Building Blocks

Structs (Data)

// Regular struct
struct Config {
    host: String,
    port: u16,
    debug: bool,
}

// Implementing methods
impl Config {
    fn new(host: String, port: u16) -> Self {
        Config { host, port, debug: false }
    }

    // Taking &self = immutable borrow of self
    fn as_url(&self) -> String {
        format!("http://{}:{}", self.host, self.port)
    }
}

Traits (Rust's version of interfaces)

// Define a trait (like an interface)
trait AIReadable {
    fn interpret(&self) -> String;
}

// Implement it for a type
impl AIReadable for Config {
    fn interpret(&self) -> String {
        format!("Config: {}:{}", self.host, self.port)
    }
}

Key point: Traits are how Rust achieves polymorphism. When reviewing AI-generated code, look for well-defined traits that describe behavior (e.g., From<String> for MyType, Display for formatting). These make code composable and easier to reason about.

Error Handling — The Rust Way

Rust doesn't have exceptions. It has Result and Option. AI agents usually get this right, but watch for these patterns:

// The standard pattern — no try/catch
let result: Result<String, std::io::Error> = std::fs::read_to_string("file.txt");

// Propagate errors with ? (the "unwrap with responsibility" operator)
fn read_config() -> Result<Config, Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string("config.json")?;  // returns on Err
    let config = serde_json::from_str(&content)?;  // another ?
    Ok(config)
}

// Option for nullable values
let maybe_value: Option<String> = map.get("key").cloned();

match maybe_value {
    Some(v) => println!("Found: {v}"),
    None => println!("Key not present"),
}

🟡 Watch out: AI agents sometimes overuse .unwrap() or .expect() — these will panic at runtime. Ask: "Should this error be propagated with ? instead?" Production Rust rarely unwraps externally-provided data.

Cargo & Project Structure — What the Agent Will Use

Cargo is Rust's build tool and package manager (like pip + make combined).

// Project structure
my-crate/
  Cargo.toml        // dependencies, package metadata
  src/
    main.rs          // binary entry point
    lib.rs           // library code (if this is a library)
    lib/               // module subdirectory (mod.rs files)

// Cargo.toml — this is what you'll care about for dependency reviews
[package]
name = "my-service"
version = "0.1.0"
edition = "2021"     // always this unless AI uses old Rust

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
reqwest = "0.11"
serde_json = "1"

Essential Cargo Commands

cargo build           // compile (slow — this is the check)
cargo run             // build + run
cargo check           // compile only (fast, for CI)
cargo test            // run all tests
cargo clippy          // linter — catches style + logic issues
cargo fmt             // auto-format (standardized code)
cargo add <crate>     // add dependency (e.g., cargo add serde)

🔴 Key crates to know for AI agent work:

tokio — async runtime (everything async uses this)

serde — serialization/deserialization (JSON, config, etc.)

tokio-serde — serde + tokio integration

reqwest — HTTP client (for AI agent API calls)

tracing — structured logging/tracing (for debugging agents)

tonic — gRPC (common in AI/ML infrastructure)

burn, candle — Rust ML frameworks (emerging)

candle — specifically from Hugging Face, good for inference

Prompting AI Agents for Rust — Practical Patterns

This is where your day-to-day work happens. Here are prompts that work well:

Prompt 1: Setting Context & Constraints You are a senior Rust engineer. I need you to write Rust code following these rules: - Use clippy and cargo fmt standards - Prefer Result over unwrap() for fallible operations - Use tokio for async code — never mix blocking and async unnecessarily - Use serde for all serialization, derive derives manually - Add #[derive(Debug, Clone, Serialize, Deserialize)] on public structs - Use thiserror for custom error types - Include async/await only where concurrency is needed - Write tests for non-trivial logic - Comment the why, not the what - Use meaningful function names — no one-letter variable names
Prompt 2: Reviewing Agent Output This Rust code was generated by an AI. Review it for: - Borrow checker correctness (moved vs borrowed, lifetime issues) - Error handling (are unwraps justified? should any be propagated?) - Concurrency (tokio task spawning, race conditions, blocking in async) - Performance (unnecessary clones, allocations in hot paths, wrong collection types) - Security (input validation, unsafe blocks, dependency trustworthiness) - Idioms (does it use standard patterns or look like Python translated to Rust?) List issues by severity: [Critical], [Important], [Style].
Prompt 3: Fixing a Compile Error This Rust code doesn't compile. The error is: [COPY THE ERROR FROM `cargo check`] The code is [...]. Fix the issue and explain what went wrong so I can avoid it in future agent prompts.
Prompt 4: Architecture Review I'm building a Rust-based AI agent runner. It needs to: - Spawn multiple agent sessions concurrently (tokio) - Handle HTTP API endpoints (axum or actix) - Read config from JSON/YAML (serde) - Log with structured tracing - Be testable and well-organized Suggest the project structure, crate layout, and key dependency choices. Explain trade-offs between common patterns (e.g., async runtime, web framework choice).

Common AI Agent Mistakes in Rust — What to Spot

🔴 Critical Issues

  • Using unsafe unnecessarily — AI sometimes throws in `unsafe { }` blocks when there's a safe alternative. Always ask: "Is there a safe way to do this?"
  • .unwrap() on external data — database reads, API responses, file I/O. These should propagate with ?
  • Data races in async code — forgetting Send/Sync bounds on tokio tasks
  • Blocking in async context — calling std::fs::read inside async fn blocks the executor. Should use tokio::fs

🟡 Common Mistakes

  • Over-cloning — cloning String everywhere instead of using &str references
  • Wrong collection — using Vec when a HashSet or BTreeMap is appropriate
  • Python-style error handling — using Option where Result is correct
  • Moving values into closures — closures that outlive the captured variables borrow forever
  • Ignoring clippy warnings — these catch real issues in idiomatic Rust

Your Learning Path (80/20 for Your Use Case)

Don't read The Book cover to cover. Focus on what enables you to guide agents effectively:

  1. Read The Book, chapters 1–10 (the free online Rust Book). Skip chapters 11–19 on testing until you actually need them. This gives you the ownership/borrowing foundation.
  2. Play with Rust Playground locally — type snippets, see compiler errors, understand why they fail. This is the fastest way to build intuition.
  3. Learn the async model — tokio, futures, async/await. This is most relevant for building agent runtimes, API servers, and concurrent systems.
  4. Understand serde deeply — it's how data moves in Rust. If you understand derive, deserialize, and custom serializers, you're set for 80% of agent code.
  5. Learn to read cargo check output — the compiler error messages are the most important skill. Learn to parse them and translate them into fixes for the agent.

Don't worry about: Complex lifetime annotations, unsafe code, FFI (unless building Python bindings), metaprogramming (procedural macros), or advanced type-level programming. These are rare in typical agent-related code.

Quick Reference: Rust- Specific Agent Prompt Tokens

Copy-paste these into your prompts when working with AI agents on Rust projects:

Quick Constraints Block — Add to Any Rust Prompt Rust standards: - Edition 2021+, never 2018 - Format: cargo fmt - Lint: clippy (default + pedantic where safe) - Error handling: thiserror for custom errors, no unwrap() on external data - Async: tokio runtime, tokio::fs not std::fs in async - Serialization: serde with derives on all public types - Logging: tracing, not println! in production code - Dependencies: pin versions, audit with cargo audit
Refinement Prompt — Use When Output Looks Off This Rust code compiles but feels unidiomatic. Review it specifically for: 1. Are we moving values when borrowing would work? 2. Should any String references be &str? 3. Is the error handling consistent (no mix of unwrap/expect with ?) 4. Are async functions actually async, or could they be synchronous? 5. Are tokio task spawns using tokio::spawn with proper bounds? 6. Is clippy happy with this code?

Resources for Ongoing Learning

The Takeaway

You're building compiler judgment, not Rust fluency. When you review agent-generated code, your job is to recognize patterns and ask the right questions:

Run cargo check and cargo clippy after every agent change. If they pass clean, you're in good shape. If they don't, paste those errors back to the agent and iterate.

Rust compiles slowly and fails loudly — that's the tradeoff. But for AI agent systems where reliability matters (agent runners, inference servers, API gateways), Rust's guarantees are hard to beat.

More guides coming soon. Ideas: Rust async deep dive, building a gRPC service for AI agents, benchmarking Rust vs Python for inference tasks.

What should we cover next?