| name | using-rust-engineering |
| description | Use when working in Rust — routes to specialist sheets for borrow-checker errors (E0502/E0597/E0382), trait bounds (E0277), async Send/Sync, clippy warnings, unsafe/FFI soundness, performance profiling, or PyO3/candle interop |
Using Rust Engineering
Overview
This meta-skill routes you to the right Rust specialist based on symptoms. Rust engineering problems fall into distinct categories that require specialized knowledge. Load this skill when you encounter Rust-specific issues but aren't sure which specialized skill to use.
Core Principle: Different Rust problems require different specialists. Match symptoms to the appropriate specialist skill. Don't guess at solutions—route to the expert.
When to Use
Load this skill when:
- Working with Rust and encountering problems
- User mentions: "Rust", "cargo", "clippy", "rustfmt", "ownership", "borrowing", "lifetimes", "traits", "generics", "unsafe", "tokio", "async", "PyO3", "bindgen", "no_std", "FFI"
- Need to implement Rust projects or optimize performance
- Setting up Rust tooling or fixing clippy warnings
- Debugging Rust code or profiling performance
- Integrating Rust with Python, C, or AI/ML pipelines
Don't use for: Non-Rust languages, general systems theory (not Rust-specific), deployment infrastructure (not Rust-specific), algorithm selection (not language-specific)
Workspace-scope concerns: For multi-crate workspaces ([workspace.dependencies], workspace-scope clippy.toml/deny.toml, public-vs-internal crate partition, version drift across the workspace, the resolver-2/3 choice, workspace anti-patterns), load /rust-workspaces (the axiom-rust-workspaces pack). This pack is single-crate-shaped; that pack composes these concerns at workspace scale.
How to Access Reference Sheets
IMPORTANT: All reference sheets are located in the SAME DIRECTORY as this SKILL.md file.
When this skill is loaded from:
skills/using-rust-engineering/SKILL.md
Reference sheets like systematic-delinting.md are at:
skills/using-rust-engineering/systematic-delinting.md
NOT at:
skills/systematic-delinting.md ← WRONG PATH
When you see a link like [systematic-delinting.md](systematic-delinting.md), read the file from the same directory as this SKILL.md.
Routing by Symptom
Modern Syntax and Editions
Symptoms:
- "What changed in Rust 2021 edition?"
- "How do I use let-else syntax?"
- "Pattern matching on references is confusing"
- "cargo fix --edition is flagging my code"
- "impl Trait in function argument position"
- "What's the difference between the 2018 and 2021 resolver?"
- "Disjoint capture rules in closures"
Route to: modern-rust-and-editions.md
Why: Rust editions change capture semantics, import syntax, and resolver behavior in ways that silently break or subtly alter code.
Example queries:
- "Upgrading from 2018 to 2021 edition, what do I check?"
- "let-else doesn't compile — what am I missing?"
- "Closure captures more than I expect in 2021"
Ownership, Borrowing, and Lifetimes
Symptoms:
- "cannot move out of borrowed content"
- "borrow of partially moved value"
- "E0597: borrowed value does not live long enough"
- "E0502: cannot borrow as mutable because it is also borrowed as immutable"
- "lifetime annotation required but I don't know where to put it"
- "cannot return reference to local variable"
- "cannot move out of
*self because it is borrowed" (E0505)
- "use of moved value" (E0382) / "cannot assign to
x because it is borrowed" (E0506)
Route to: ownership-borrowing-lifetimes.md
Why: Ownership and lifetime errors are Rust's most common stumbling block and require systematic mental models, not ad-hoc fixes.
Example queries:
- "Getting E0502 on mutable borrow inside a loop"
- "How do I express 'output lifetime tied to input'?"
- "Struct holds a reference and the compiler rejects my lifetime annotation"
Traits, Generics, and Dispatch
Symptoms:
- "E0277: the trait bound
T: Foo is not satisfied"
- "E0225: only auto traits can be used as additional bounds"
- "dyn Trait vs impl Trait — when to use which?"
- "associated types vs generic parameters"
- "cannot use
+ to add bounds on object-safe trait"
- "Higher-ranked trait bounds (for<'a>)"
- "Trait not implemented for reference type"
Route to: traits-generics-and-dispatch.md
Why: Rust's trait system—object safety, dispatch mechanics, HRTB—has non-obvious rules that require a dedicated reference to navigate correctly.
Example queries:
- "E0277 when calling a generic function with my type"
- "How do I write a function that accepts any type implementing Iterator?"
- "When should I box a trait object vs use generics?"
Error Handling
Symptoms:
- "E0277:
? operator can't convert between error types"
- "Boilerplate
match on every Result is exhausting"
- "When to use
anyhow vs thiserror?"
- "How to define my own error type with context?"
- "Propagating errors through async functions"
- "Unwrap vs expect vs propagate — best practice?"
- "Error variant has no Display impl"
Route to: error-handling-patterns.md
Why: Idiomatic Rust error handling involves a hierarchy of strategies (custom types, anyhow, thiserror, ?) that must be matched to the codebase's ownership model and API surface.
Example queries:
- "Library crate — should I use anyhow or define my own error type?"
- "How to add context to errors with
? operator?"
- "Converting between foreign error types without From boilerplate"
Project Setup and Tooling
Symptoms:
- "How do I structure a Cargo workspace?"
- "Conditional compilation with feature flags"
- "Cargo.toml dependency resolution conflicts"
- "How to set up pre-commit hooks with rustfmt and clippy?"
- "Build scripts (build.rs) basics"
- "Cross-compilation setup"
- "Publishing a crate to crates.io"
Route to: project-structure-and-tooling.md
Why: Cargo workspaces, feature flags, and build scripts interact in ways that require an end-to-end setup guide rather than piecemeal answers.
Example queries:
- "Setting up a monorepo with shared library crates"
- "How do I enable a feature only in tests?"
- "cargo build fails after adding a C dependency"
Note: This pack's project-structure sheet is single-crate-shaped. For workspace-scope concerns (multi-crate composition, [workspace.dependencies], workspace-scope clippy.toml / deny.toml, public-vs-internal crate boundaries, version drift across the workspace) load /rust-workspaces (the axiom-rust-workspaces pack) instead.
Workspace-Scope Concerns
Symptoms:
- "Two crates in our workspace pin the same dep at different versions"
- "
cargo build and cargo build -p some-crate produce different binaries"
- "Should we be on resolver 2 or 3?"
- "How do we declare lints once for the whole workspace?"
- "Where does
deny.toml live in a workspace?"
- "Should this be a separate crate, or stay a module?"
- "An internal crate accidentally got published to crates.io"
- "We have ten crates and each has its own clippy config — how do we consolidate?"
- "Can a public crate
pub use a type from an internal crate?"
- "We have a
[workspace] block with one member — is that fine?"
Route to: /rust-workspaces (the axiom-rust-workspaces pack — separate skill, different pack)
Why: This pack's sheets assume one crate's perspective. Workspace-scope decisions (structure, dep unification under the resolver, workspace-wide lint/deny policy, publish/internal partition, anti-pattern sweeps) are a different axis and live in axiom-rust-workspaces. Each crate in a workspace passes this pack's bar; the workspace as a whole passes the workspaces pack's gate.
Example queries:
- "Set up a new multi-crate Rust project from scratch"
- "We're splitting our single crate into a workspace — what's the structure?"
- "Audit our existing workspace for drift and accidental publishing"
- "Decide which of our crates should be public on crates.io"
Testing
Symptoms:
- "Tests are not running"
- "How to test private functions in Rust?"
- "Integration test can't see my module"
- "How to mock traits in unit tests?"
- "Property-based testing with proptest or quickcheck"
- "Test coverage setup with llvm-cov"
- "cargo test hangs or produces unexpected output"
Route to: testing-and-quality.md
Why: Rust's test module system, integration test layout, and mocking patterns differ from other languages and require specific structural knowledge.
Example queries:
- "Test module in same file vs tests/ directory — when to use which?"
- "How to mock a trait dependency without a framework?"
- "Set up cargo-llvm-cov for CI coverage reporting"
Lint Warnings and Delinting
Symptoms:
- "clippy warns
too_many_arguments"
- "Hundreds of clippy warnings in legacy crate"
- "How to fix
clippy::expect_used across a large codebase?"
- "Suppress clippy without disabling the lint globally"
- "clippy pedantic — where to start?"
- "Systematic approach to reducing clippy warnings"
- "rustfmt reformats entire files on first run"
Route to: systematic-delinting.md
Why: Reducing clippy warnings at scale requires a staged methodology—triaging, grouping, fixing by category—to avoid churn and accidental regressions.
Example queries:
- "500+ clippy warnings, where to start?"
- "Fix clippy warnings without disabling them"
- "How to handle allow attributes without silencing real issues?"
Note: If setting UP linting (not fixing), route to project-structure-and-tooling.md first.
Async and Concurrency
Symptoms:
- "future is not
Send because it contains a MutexGuard"
- "tokio task panics with 'called
Option::unwrap() on a None value'"
- "blocking call inside async function"
- "E0277:
impl Future not satisfied"
- "How to use
tokio::select! correctly?"
- "Async trait not object-safe"
- "Spawned task borrows data that doesn't live long enough"
Route to: async-and-concurrency.md
Why: Rust async adds a Send/Sync layer on top of ownership and lifetime rules; tokio's runtime model, task spawning, and executor requirements create failure modes that require dedicated treatment.
Example queries:
- "Future is not Send because I hold a Mutex guard across await"
- "How to call blocking code from async code in tokio?"
- "tokio::spawn requires 'static — how do I share data with the task?"
Performance and Profiling
Symptoms:
- "cargo flamegraph shows hotspot in allocator"
- "Release build still slow"
- "Memory usage is higher than expected"
- "How to profile Rust code on Linux?"
- "Vec allocations are dominating CPU"
- "SIMD / auto-vectorization not kicking in"
- "Benchmark with criterion — setup and interpretation"
Route to: performance-and-profiling.md
Why: Rust performance work requires profiling before optimization; the allocator, inlining, and LLVM pass pipeline all interact in ways that demand measurement-driven decisions.
Example queries:
- "Release binary is slow — how do I find the bottleneck?"
- "Set up criterion benchmarks for a hot function"
- "cargo flamegraph shows 40% time in jemalloc — what next?"
Unsafe, FFI, and Low-Level
Symptoms:
- "undefined behavior in unsafe block"
- "bindgen fails on opaque type"
- "maturin develop fails with symbol not found"
- "How to write safe wrappers around C APIs?"
- "
no_std crate — which allocator to use?"
- "Miri reports use-after-free in my unsafe code"
- "Raw pointer arithmetic — alignment rules"
Route to: unsafe-ffi-and-low-level.md
Why: Unsafe Rust, C FFI, and no_std environments require precise knowledge of invariants, ABI contracts, and platform constraints that cannot be safely approximated.
Example queries:
- "Writing a C binding — how to handle nullable pointers safely?"
- "no_std embedded target with custom allocator"
- "How to use Miri to validate my unsafe code?"
AI/ML and Interop
Symptoms:
- "PyO3 extension panics when called from Python"
- "maturin develop fails with linking errors"
- "How to call a Rust inference engine from Python?"
- "tch-rs (PyTorch) tensor operations — memory layout"
- "candle model integration — device management"
- "ndarray vs nalgebra for numerical kernels"
- "How to expose a Rust struct to Python with PyO3?"
Route to: ai-ml-and-interop.md for the AI/ML framework choices (candle, burn, tch-rs, ndarray, nalgebra) and basic PyO3 onboarding.
For production-grade PyO3 work, redirect to /pyo3-interop (the axiom-pyo3-interop pack). That pack treats the Python ↔ Rust FFI boundary as a discipline distinct from per-crate Rust work — GIL release patterns, abi3 vs native and the wheel matrix, maturin inside a Cargo workspace, batched FFI to amortise crossing cost, zero-copy NumPy via the buffer protocol, Gymnasium environments backed by Rust, error mapping with traceback fidelity, lifecycle and teardown at interpreter shutdown, async across the boundary (pyo3-async-runtimes, tokio + asyncio), packaging and wheels (cibuildwheel, manylinux), debugging boundary panics / segfaults / GIL deadlocks, and the cost-of-crossing performance model. Load that pack when the FFI surface is the hot path (RL self-play, simulation engines, training data pipelines, inference servers); this sheet is sufficient for one-off bindings and ML-framework selection.
Why: Rust's role in AI/ML pipelines—as Python extension via PyO3, as a performance kernel, or as a standalone inference host—introduces cross-language ownership and memory layout constraints not covered by either the Rust or ML literature alone. Once the Python surface becomes more than a thin convenience binding, those constraints deserve their own pack.
Example queries:
- "Build a PyO3 extension that processes NumPy arrays" → start here; redirect to
/pyo3-interop for production hardening.
- "Integrate candle model into a Python service" → start here for the candle side,
/pyo3-interop for the binding.
- "Zero-copy tensor sharing between Rust and Python" →
/pyo3-interop:numpy-buffer-protocol.
- "GIL deadlock in our PyO3 module" →
/pyo3-interop:gil-release-patterns and /pyo3-interop:debugging-pyo3.
- "Wheel matrix for PyO3 module across CPython 3.9–3.13" →
/pyo3-interop:abi3-vs-native-extensions and /pyo3-interop:packaging-and-wheels.
Edge Cases
Ambiguous Symptom Resolution
Some symptoms cross multiple specialist domains. Use the following priority rules:
"Unsafe async code" (e.g., raw pointer passed across await points):
- Start with unsafe-ffi-and-low-level.md — confirm invariants and soundness first
- Then async-and-concurrency.md — address Send/Sync requirements and executor interaction
"clippy warnings inside unsafe blocks":
- Start with unsafe-ffi-and-low-level.md — unsafe code has different correctness rules than safe Rust
- Then systematic-delinting.md — apply lint suppression strategy after understanding the safety invariants
"PyO3 + lifetime errors":
- Start with ownership-borrowing-lifetimes.md — resolve the Rust-side lifetime model first
- Then ai-ml-and-interop.md — apply PyO3-specific patterns (GIL management,
Python<'py> tokens)
"Async trait not object-safe":
- Start with traits-generics-and-dispatch.md — understand object safety rules
- Then async-and-concurrency.md — apply
async_trait crate or BoxFuture patterns
"Performance regression after adding generics":
- Start with performance-and-profiling.md — measure before drawing conclusions
- Then traits-generics-and-dispatch.md — consider monomorphization cost, dynamic dispatch trade-off
"Ambiguous general query" (e.g., "my Rust code is broken"):
→ Ask: "What specific issue? Borrow checker errors? Compile-time trait errors? Runtime panics? Performance? Clippy warnings?"
Never guess when ambiguous. Ask once, route accurately.
Cross-Cutting Scenarios
Some tasks span multiple specialists and must be loaded in a specific order. Setup before fixes, diagnosis before optimization, soundness before polish.
New Rust project with FFI (greenfield crate that will call C or be called from C):
- project-structure-and-tooling.md — workspace layout,
build.rs, feature flags, links= metadata
- unsafe-ffi-and-low-level.md — bindgen/cbindgen, ABI contracts, safe wrapper patterns
- testing-and-quality.md — Miri, integration tests across the FFI boundary
Legacy crate cleanup (inherited codebase, no lint config, warnings everywhere):
- project-structure-and-tooling.md — establish
[lints] table, rustfmt config, CI gates
- systematic-delinting.md — staged clippy reduction by category
- testing-and-quality.md — coverage baseline with cargo-llvm-cov before larger refactors
Slow release binary (production binary missing performance targets):
- performance-and-profiling.md — measure first (flamegraph, criterion, heaptrack)
- Then EITHER async-and-concurrency.md (if hotspot is runtime/executor) OR traits-generics-and-dispatch.md (if dynamic dispatch or monomorphization is hot)
Adding a Python binding to an existing Rust crate (exposing a mature Rust library via PyO3):
- ownership-borrowing-lifetimes.md — fix the Rust-side lifetime model before crossing languages
- unsafe-ffi-and-low-level.md — understand the FFI boundary and invariants
- ai-ml-and-interop.md — PyO3 patterns (
Python<'py>, GIL, NumPy buffer protocol)
Migrating to edition 2024 (upgrading edition on an established crate):
- modern-rust-and-editions.md — capture rule changes,
cargo fix --edition, resolver differences
- traits-generics-and-dispatch.md — dyn-compatibility changes,
impl Trait precise capture
- async-and-concurrency.md — async trait changes (
async fn in trait, RPITIT interactions)
Load in order of execution: Setup before optimization, diagnosis before fixes, soundness before polish.
Common Routing Mistakes
| Symptom | Wrong Route | Correct Route | Why |
|---|
| "Code slow" | traits-generics | performance-and-profiling FIRST | Don't optimize without profiling |
| "Setup clippy and fix warnings" | systematic-delinting only | project-structure THEN delinting | Setup before fixing |
| "E0277 in async function" | async-and-concurrency | traits-generics-and-dispatch | Trait bound errors are a type system problem |
| "unsafe lifetime error" | ownership-borrowing-lifetimes only | unsafe-ffi THEN ownership | Soundness before lifetime annotation |
| "PyO3 panic at runtime" | ai-ml-and-interop only | unsafe-ffi or ownership first | Find the UB/lifetime root cause first |
| "Fix 500 clippy warnings" | project-structure | systematic-delinting | Process for fixing, not setup |
Key principle: Diagnosis before solutions, setup before optimization, measure before performance fixes.
Red Flags — Stop and Route
If you catch yourself about to:
All of these mean: You're about to give incomplete advice. Route to the specialist instead.
Common Rationalizations (Don't Do These)
| Excuse | Reality | What To Do |
|---|
| "Just a simple borrow-checker fix" | Ad-hoc fixes mask the real ownership issue and recur later | Route to ownership-borrowing-lifetimes.md |
| "User knows Rust, skip routing" | Experience in safe Rust ≠ expertise in async Send/Sync or unsafe invariants | Route based on symptom, not assumed skill |
"I'll just add .clone()" | That's the anti-pattern the specialist exists to prevent | Route to ownership-borrowing-lifetimes.md for the systematic alternative |
| "Quick lifetime annotation" | Sprinkling 'a without a model creates compounding problems | Route to ownership-borrowing-lifetimes.md |
"One clippy allow won't hurt" | Allows accumulate; that's exactly why the delinting sheet exists | Route to systematic-delinting.md |
| "unsafe is fine here, I checked" | Unsafe requires documented invariants, not gut-check | Route to unsafe-ffi-and-low-level.md |
If you catch yourself thinking ANY of these, STOP and route to the specialist.
Red Flags Checklist — Self-Check Before Answering
Before giving ANY Rust advice, ask yourself:
-
❓ Did I identify the symptom?
- If no → Read the query again, identify the symptom (error code, tool output, runtime behavior)
-
❓ Is this symptom in my routing table?
- If yes → Route to that specialist
- If no → Ask a clarifying question
-
❓ Am I about to give advice directly?
- If yes → STOP. Why am I not routing?
- Check the rationalization table — am I making excuses?
-
❓ Is this a diagnosis issue or a solution issue?
- Diagnosis (performance, runtime panic, "something is wrong") → Route to profiling/diagnostic skill FIRST
- Solution (known symptom, known category) → Route to the appropriate specialist
-
❓ Is the query ambiguous?
- If yes → Ask ONE clarifying question
- If no → Route confidently
-
❓ Am I feeling pressure to skip routing?
- Route anyway. Time pressure, apparent complexity, user confidence, and "this is simple" all produce the same bad outcome — a direct answer that misses the specialist's checklist. Specialists exist precisely for the cases that feel like they don't need them.
If you failed ANY check above, do NOT give direct advice. Route to the specialist or ask a clarifying question.
Rust Engineering Specialist Skills
After routing, load the appropriate specialist skill for detailed guidance:
- modern-rust-and-editions.md — Rust editions, let-else, pattern matching improvements, resolver changes
- ownership-borrowing-lifetimes.md — Borrow checker mental models, lifetime annotation, common E05xx errors
- traits-generics-and-dispatch.md — Trait bounds, object safety, HRTB, monomorphization vs dynamic dispatch
- error-handling-patterns.md — anyhow, thiserror, custom error types,
? operator, error context
- project-structure-and-tooling.md — Cargo workspaces, feature flags, build.rs, cross-compilation, publishing
- testing-and-quality.md — Test module layout, integration tests, mocking traits, proptest, cargo-llvm-cov
- systematic-delinting.md — Staged clippy warning reduction without allow-attribute proliferation
- async-and-concurrency.md — tokio, async traits, Send/Sync, structured concurrency, blocking in async
- performance-and-profiling.md — cargo flamegraph, criterion, allocator profiling, SIMD, inlining
- unsafe-ffi-and-low-level.md — Unsafe invariants, bindgen, Miri, no_std, raw pointer rules
- ai-ml-and-interop.md — PyO3, maturin, tch-rs, candle, ndarray, zero-copy Python/Rust interop
When NOT to Use Rust Skills
Skip Rust pack when:
- Non-Rust language (use appropriate language pack)
- Algorithm selection without Rust-specific context (use CS / algorithms pack)
- Infrastructure/deployment (use DevOps/infrastructure pack)
- General ML model design (use Yzmir AI/ML pack)
Rust pack is for: Rust-specific implementation, tooling, borrow checker, traits, async, FFI, performance, and AI/ML interop.
Diagnosis-First Principle
Critical: Many Rust issues require diagnosis before solutions:
| Issue Type | Diagnosis Skill | Then Solution Skill |
|---|
| Performance | performance-and-profiling (flamegraph, heaptrack) | async-and-concurrency or performance-and-profiling |
| Runtime panic (unwrap, index out of bounds) | ownership-borrowing-lifetimes | error-handling-patterns or unsafe-ffi-and-low-level |
| Trait errors in async | traits-generics-and-dispatch | async-and-concurrency |
| Test failure of unclear cause | testing-and-quality | the relevant domain skill once isolated |
If unclear what's wrong, route to diagnostic skill first.
Integration Notes
Phase 1 — Standalone: Rust skills are self-contained.
Future cross-references:
- superpowers:test-driven-development (TDD methodology before implementing)
- superpowers:systematic-debugging (systematic debugging before profiling)
- yzmir-ai-engineering-expert (for ML model design separate from Rust integration)
Current focus: Route within Rust pack only. Other packs handle other concerns.