AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Rust Advanced

skill-trancong12102-agentskills-rust-advanced · by trancong12102

Advanced Rust patterns for ownership, traits, async, error handling, macros, type system tricks, unsafe, and performance. Use when tackling complex Rust problems — not basic syntax, but multi-concern tasks like designing cancellation-safe async services, choosing between trait objects and generics, building typestated APIs, structuring error hierarchies across crate boundaries, writing proc macro…

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-trancong12102-agentskills-rust-advanced

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-trancong12102-agentskills-rust-advanced)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Rust Advanced? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Rust Advanced: Patterns, Conventions & Pitfalls

This skill defines rules, conventions, and architectural decisions for building production Rust applications. It is intentionally opinionated to prevent common pitfalls and enforce patterns that scale.

For detailed API documentation of any crate mentioned here, use other appropriate tools (documentation lookup, web search, etc.) — this skill focuses on how and why to use these patterns, not full API surfaces.

Ownership & Borrowing Rules

Interior mutability — decision flowchart

Need shared mutation?
  YES → Single-threaded or multi-threaded?
    Single-threaded → Is T: Copy?
      YES → Cell (zero overhead, no borrow tracking)
      NO  → RefCell (runtime borrow checking, panics on violation)
    Multi-threaded → High contention?
      NO  → Arc> (simple, correct)
      YES → Arc> (many readers, few writers)
             or lock-free types (crossbeam, atomic)
  NO → Use normal ownership / borrowing

Smart pointer selection

| Type | When to use | | ------------- | -------------------------------------------------------- | | Box | Recursive types, large stack values, trait objects | | Rc | Single-threaded shared ownership (trees, graphs) | | Arc | Multi-threaded shared ownership | | Cow | Sometimes borrowed, sometimes owned — avoid eager clones | | Pin> | Self-referential types, async futures |

The Cow rule

Accept Cow or Cow when a function sometimes modifies its input and sometimes passes it through unchanged. This avoids allocating when no modification is needed. Prefer &str in function arguments when you never need ownership.


Error Handling Strategy

The golden rule: libraries use thiserror, applications use anyhow

| Context | Crate | Why | | -------------------- | ----------- | -------------------------------------------------------- | | Library crate | thiserror | Callers need to match on specific error variants | | Binary / application | anyhow | Errors bubble up to user-facing messages with context | | Internal modules | thiserror | Type-safe error variants for the parent module to handle | | FFI boundary | Custom enum | Must map to C-compatible error codes |

Required patterns

  1. Always add context when propagating with ? in application code:

``rust fs::read_to_string(path) .with_context(|| format!("failed to read config: {path}"))?; ``

  1. Use #[from] for automatic conversions in library error enums:

``rust #[derive(thiserror::Error, Debug)] pub enum DbError { #[error("connection failed: {0}")] Connection(#[from] std::io::Error), #[error("query failed: {reason}")] Query { reason: String }, } ``

  1. Prefer Result combinators over nested match for short chains:

map, map_err, and_then, unwrap_or_else.

  1. Never unwrap() in library code. Use expect() only when the invariant

is documented and provably upheld.


Trait System Conventions

Trait objects vs generics — decision rule

Need runtime polymorphism (heterogeneous collection, plugin system)?
  YES → dyn Trait (Box or &dyn Trait)
  NO  → impl Trait / generics (zero-cost, monomorphized)

Key patterns

  • Associated types over generics when there is exactly one natural

implementation per type (e.g., Iterator::Item).

  • Sealed traits when you need to prevent downstream crates from implementing

your trait — essential for semver stability.

  • Blanket implementations to extend functionality to all types satisfying a

bound (e.g., impl ToString for T).

  • Supertraits when your trait logically requires another trait's guarantees

(e.g., trait Printable: Debug + Display).

Object safety rules

A trait is object-safe (can be used as dyn Trait) only if:

  • No methods return Self
  • No methods have generic type parameters
  • All methods take self, &self, or &mut self

If you need dyn Trait + async, use #[async_trait] or return Box manually — native async in traits is not yet object-safe.


Async Rust Rules

Runtime: Tokio is the default

Use tokio with #[tokio::main] and #[tokio::test]. For CPU-bound work inside an async context, use tokio::task::spawn_blocking or rayon.

Native async traits — drop #[async_trait] where possible

Since Rust 1.75, async fn in traits works natively. Use native syntax unless you need dyn Trait with async methods.

The Send/Sync rule

Futures passed to tokio::spawn must be Send. The #1 cause of non-Send futures: holding a MutexGuard (or any !Send type) across an .await point.

Fix: drop the guard before awaiting, or scope the lock in a block:

{
    let mut guard = lock.lock().unwrap();
    guard.push(42);
} // guard dropped
do_async_thing().await; // future is Send

Cancellation safety — the most dangerous async footgun

Any future can be dropped at any .await point (especially in tokio::select!). Know which operations are cancel-safe:

| Operation | Cancel-safe? | | ---------------------------- | ------------ | | mpsc::Receiver::recv | Yes | | AsyncReadExt::read | Yes | | AsyncWriteExt::write_all | No | | AsyncBufReadExt::read_line | No |

For cancel-unsafe code: wrap in tokio::spawn (dropping a JoinHandle does not cancel the spawned task) or use tokio_util::sync::CancellationToken for cooperative cancellation.

Structured concurrency: use JoinSet

let mut set = tokio::task::JoinSet::new();
for url in urls {
    set.spawn(fetch(url));
}
while let Some(result) = set.join_next().await {
    result??;
}

Type System Patterns

Newtype — zero-cost domain types

Wrap primitives to create distinct types. Prevents mixing UserId with OrderId:

struct UserId(u64);
struct OrderId(u64);
// fn process(user: UserId, order: OrderId) — compiler prevents swaps

Typestate — compile-time state machine

Encode lifecycle states as type parameters. Invalid transitions become compile errors:

struct Connection { socket: TcpStream, _state: PhantomData }
struct Disconnected;
struct Connected;

impl Connection {
    fn connect(self) -> Result> { ... }
}
impl Connection {
    fn send(&self, data: &[u8]) -> Result { ... }
    // send() is unavailable on Connection
}

Const generics — array sizes as type parameters

struct Matrix {
    data: [[f64; COLS]; ROWS],
}
impl Matrix {
    fn trace(&self) -> f64 { (0..N).map(|i| self.data[i][i]).sum() }
}

PhantomData variance

| Marker | Variance | Use for | | ------------------------- | ------------- | ----------------------- | | PhantomData | Covariant | "Owns" a T conceptually | | PhantomData | Contravariant | Consumes T (rare) | | PhantomData T> | Invariant | Must be exact type | | PhantomData | Invariant | Raw pointer semantics |


Performance Decision Framework

Is this a hot path (profiled, not guessed)?
  NO  → Write clear, idiomatic code. Don't optimize.
  YES → Which bottleneck?
    CPU-bound computation → rayon::par_iter() for data parallelism
    Many small allocations → Arena allocator (bumpalo)
    Iterator chain not vectorizing → Check for stateful dependencies,
      use fold/try_fold, or restructure as plain slice iteration
    Cache misses → #[repr(C)] + align, struct-of-arrays layout
    Heap allocation → Box instead of Vec when size is fixed,
      stack allocation for small types, SmallVec for usually-small vecs

The zero-cost rule

Iterator chains (filter().map().sum()) compile to the same code as hand-written loops — prefer them for readability. But stateful iterator chains can block auto-vectorization; see references/performance.md for SIMD details.


Unsafe Policy

  1. Minimize scope — wrap only the minimum number of lines in unsafe {}.
  2. Mandatory // SAFETY: comment on every unsafe block explaining

why the invariants are upheld.

  1. Prefer safe abstractionsas casts, bytemuck::cast, from_raw_parts

over transmute. Use transmute only as last resort with turbofish syntax.

  1. FFI boundary rule: generate bindings with bindgen, wrap in a thin safe

Rust API, document every invariant.

  1. Never use unsafe to bypass the borrow checker. If you think you need to,

redesign the data structure.


Common Pitfalls

  1. Holding MutexGuard across .await — makes the future !Send, breaks

tokio::spawn. Scope the lock in a block before awaiting.

  1. RefCell double borrow panicborrow_mut() panics if any borrow is

live. Use try_borrow_mut() when borrow lifetimes aren't fully controlled.

  1. Mutex deadlock — Rust's Mutex is non-reentrant. Never lock the same

mutex twice on one thread. Acquire multiple locks in consistent order.

  1. collect::>>() vs collect::, E>>()

the second form fails fast on first error and is almost always what you want.

  1. Accepting &String instead of &str&String auto-derefs to &str

but not vice versa. Always accept &str in function signatures.

  1. unwrap() in library code — crashes the caller. Use ? with proper

error types, or expect() with documented invariant.

  1. Forgetting #[must_use] on Result-returning functions — callers may

silently ignore errors. The compiler warns, but custom types need the attribute.

  1. Using std::sync::Mutex in async code — blocks the executor thread.

Use tokio::sync::Mutex for async contexts.

  1. String::from in hot loops — allocates each iteration. Pre-allocate

with String::with_capacity() or use Cow.

  1. Ignoring cancellation safety in select! — the non-winning future is

dropped. Cancel-unsafe operations lose data silently.

  1. clone() as first instinct — usually a sign of fighting the borrow

checker. Restructure ownership or use references first.

  1. Box instead of proper error enum — loses the ability to

match on specific variants. Use thiserror for structured errors.


Reference Files

Read the relevant reference file when working with a specific topic:

| File | When to read | | ------------------------------ | -------------------------------------------------------------- | | references/ownership.md | Interior mutability, smart pointers, Cow, Pin, lifetime tricks | | references/traits.md | Trait objects, sealed traits, blanket impls, HRTB, variance | | references/error-handling.md | thiserror v2, anyhow, Result combinators, error design | | references/async-rust.md | Tokio runtime, cancellation, JoinSet, Send/Sync, select! | | references/performance.md | Zero-cost, SIMD, arena allocation, rayon, cache optimization | | references/unsafe-ffi.md | Unsafe superpowers, FFI with bindgen, transmute, raw pointers | | references/macros.md | Declarative macros, proc macros, derive macros, syn/quote | | references/type-patterns.md | Newtype, typestate, PhantomData, const generics, builder |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.