Skip to content
v0.1.1latest release34 skills2 categories4 sub-categories79 tested scripts157 reference docsContact us

rust-trait-design-reviewer

Review and design Rust traits and generic APIs: generics vs impl Trait vs dyn Trait (static vs dynamic dispatch), dyn compatibility / object safety (generic methods, Self returns, where Self: Sized, error[E0038] not dyn compatible / cannot be made into an object), associated types vs generic parameters, blanket impls, coherence and orphan rule (E0117, E0119, E0210), newtype, extension, sealed and marker traits, supertraits and trait upcasting (1.86), async fn in traits (1.75) and the Send bound problem (trait_variant, async-trait), RPITIT, GATs, From/Into/TryFrom/AsRef/Borrow, std trait checklist (Debug, Clone, PartialEq, Hash, Default, Display, Error, Send/Sync, serde feature), #[must_use], #[non_exhaustive], semver hazards, Deref abuse, builders, typestate, PhantomData, Box<dyn Error + Send + Sync>, Fn traits, mockall. Use when designing a public Rust trait or API, reviewing a PR adding traits/impls, or fixing E0038, E0117, E0119, E0210, E0277, E0599 or 'future cannot be sent between threads safely'.

Programming / Rustv1.0.0Updated OCT 21 20267 references7 scripts

Design traits that are easy to implement, hard to misuse and safe to evolve, and review existing trait-heavy code against the Rust API Guidelines. Every decision is a trade-off between three axes: dispatch (generics vs impl Trait vs dyn Trait), extensibility (who may implement the trait: anyone, nobody outside the crate, or only via a blanket impl) and evolution (which later changes are semver-major). Small std-only Rust programs in scripts/ scan source text for the mistakes that are cheap to catch mechanically (dyn-incompatible traits used as dyn, impl Into, missing Debug/Error impls, Deref inheritance, non-Send Box<dyn Error>, &Vec<T> parameters, unsendable public async fn traits), and scaffold_trait prints compiling templates for sealed, extension, typestate, newtype, dyn-compatible and async-Send traits.

Trigger Conditions

Activate this skill when the user:

  • Designs a public trait, a plugin/strategy/backend abstraction or a generic library API in Rust
  • Asks "generics or dyn?", "impl Trait or T: Trait?", "associated type or generic parameter?"
  • Hits error[E0038]: the trait X is not dyn compatible / cannot be made into an object, E0117 / E0210 (orphan rule), E0119 (conflicting implementations), E0277 (trait bound not satisfied), E0599 (method exists but trait bounds not satisfied / trait not in scope), E0191 (associated type must be specified), E0225
  • Uses async fn in a trait and sees future cannot be sent between threads safely or the async_fn_in_trait warning, or needs dyn with async methods
  • Reviews a PR adding traits, blanket impls, Deref, From/Into, error types or builder APIs
  • Wants to seal a trait, add an extension trait, a newtype, a typestate builder or a mockable trait
  • Asks which std traits a public type should implement, or whether a change is semver-breaking

Workflow

text
0. BUILD     -> once: for t in dyn_compat trait_shape api_traits api_signatures scaffold_trait; do
                  rustc -O --edition 2021 "$SKILL/scripts/$t.rs" -o "/tmp/$t"; done
1. SCAN      -> for t in dyn_compat trait_shape api_traits api_signatures; do /tmp/$t src/; done
                (whole crate: impls and `dyn` uses in other files count)
2. CLASSIFY  -> For each trait: who calls it (generic code, dyn, both)? who implements it (us, users, tests)?
                Use the dispatch and extensibility tables below.
3. CHECK     -> dyn compatibility, coherence (could a user or upstream crate write a conflicting impl?),
                Send/Sync story, std trait checklist for every public type, semver surface
4. FIX       -> Smallest change that keeps call sites compiling: `where Self: Sized`, From instead of Into,
                derive Debug, thiserror, &[T] instead of &Vec<T>, seal before 1.0
                Templates: /tmp/scaffold_trait sealed --name Backend
5. VERIFY    -> cargo check && cargo clippy -- -W clippy::pedantic; cargo test (incl. compile_fail doctests);
                cargo semver-checks for published crates; each scanner with --strict exits 1 on findings

Dispatch: Generics vs impl Trait vs dyn Trait

NeedUseCost / limit
Fastest call, inlining, caller picks the typefn f<T: Trait>(x: T)one copy per type (code size, compile time); callers can turbofish f::<u8>
Same, simpler signaturefn f(x: impl Trait)identical codegen; no turbofish for that parameter
Return one concrete unnamed type (closures, iterators)fn f() -> impl Iterator<Item = u32>one hidden type per function; auto traits (Send) leak from the body
Heterogeneous collection, plugin registry, runtime choiceVec<Box<dyn Trait>>, &dyn Trait, Arc<dyn Trait + Send + Sync>vtable call, no inlining, trait must be dyn compatible
Reduce monomorphization bloat of a big generic fngeneric shell + non-generic inner fn taking &dyn/&Pathone extra call
Heterogeneous but closed set of typesenum with one variant per typeno vtable, exhaustive match; adding a type = editing the enum
rust
use std::fmt::Display;
use std::path::Path;

pub fn join_all<T: Display>(items: &[T]) -> String {
    items.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
}

pub fn bracket(item: impl Display) -> String {
    format!("[{item}]")
}

pub fn join_mixed(items: &[&dyn Display]) -> String {
    items.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
}

// Generic shell, non-generic body: only the tiny shell is monomorphized (std::fs::read does this).
pub fn line_count<P: AsRef<Path>>(path: P) -> std::io::Result<usize> {
    fn inner(path: &Path) -> std::io::Result<usize> {
        Ok(std::fs::read_to_string(path)?.lines().count())
    }
    inner(path.as_ref())
}

fn main() {
    let mixed: [&dyn Display; 3] = [&1, &"two", &3.5];
    println!("{} | {} | {}", join_all(&[1, 2, 3]), bracket("x"), join_mixed(&mixed));
}

Dyn Compatibility (Object Safety)

A trait can be used as dyn Trait only if every method can be called through a vtable. Methods that cannot must opt out with where Self: Sized (they are then callable on concrete types only).

Breaks dyn (E0038)Fix
trait T: Sized, or supertrait Clone, Default, Copy, FromStr (all require Sized)drop the supertrait; for Clone add fn box_clone(&self) -> Box<dyn T> (or the dyn-clone crate)
supertrait PartialEq/Eq/PartialOrd/Ord (they use Self as a type parameter)compare via a method returning a key, or downcast with Any
generic method fn put<V: Serialize>(&mut self, v: &V) or impl Trait argumentwhere Self: Sized, or a dyn-friendly variant taking &dyn Erased/bytes
Self in argument or return type: fn merge(&self, other: Self), fn dup(&self) -> Selfwhere Self: Sized, or return Box<dyn T>
associated function without receiver: fn new() -> Selfwhere Self: Sized
async fn / -> impl Trait method (RPITIT)#[async_trait], or return Pin<Box<dyn Future<Output = T> + Send + '_>> / Box<dyn Iterator>
associated const, or generic associated type type Item<'a>move the const to a method; split the GAT into a separate trait

Allowed: lifetime parameters on methods, trait-level generic parameters (dyn Codec<u32>), associated types (must be named: dyn Iterator<Item = u8>, else E0191), receivers &self, &mut self, self: Box<Self>, Rc<Self>, Arc<Self>, Pin<&mut Self>. Only auto traits may be added: dyn Read + Send works, dyn Read + Write is E0225 (make trait ReadWrite: Read + Write {} with a blanket impl).

rust
use std::fmt::Debug;

pub trait Storage: Debug + Send + Sync {
    fn get(&self, key: &str) -> Option<Vec<u8>>;
    fn put_bytes(&mut self, key: &str, value: &[u8]);

    fn open(path: &str) -> std::io::Result<Self>
    where
        Self: Sized;

    fn put_all<I: IntoIterator<Item = (String, Vec<u8>)>>(&mut self, items: I)
    where
        Self: Sized,
    {
        for (k, v) in items {
            self.put_bytes(&k, &v);
        }
    }
}

#[derive(Debug, Default)]
pub struct Memory(std::collections::HashMap<String, Vec<u8>>);

impl Storage for Memory {
    fn get(&self, key: &str) -> Option<Vec<u8>> {
        self.0.get(key).cloned()
    }
    fn put_bytes(&mut self, key: &str, value: &[u8]) {
        self.0.insert(key.to_owned(), value.to_vec());
    }
    fn open(_path: &str) -> std::io::Result<Self> {
        Ok(Self::default())
    }
}

fn main() {
    let mut store: Box<dyn Storage> = Box::new(Memory::open("mem").unwrap());
    store.put_bytes("k", b"v");
    assert_eq!(store.get("k").as_deref(), Some(&b"v"[..]));
}

Trait upcasting (Rust 1.86): &dyn Sub coerces to &dyn Super (also Box, Arc) when trait Sub: Super. With trait Plugin: Any, let any: &dyn Any = plugin; then any.downcast_ref::<T>() works without an as_any() helper. Before 1.86 add fn as_any(&self) -> &dyn Any.

Associated Types vs Generic Parameters

QuestionAssociated type type Output;Generic parameter Trait<T>
How many impls per implementing type?exactly one (Iterator::Item, Deref::Target)many (From<T>, Add<Rhs>, AsRef<T>)
Who chooses?the implementorthe caller / the impl list
Inferencealways determined by Selfmay need annotations (E0283 type annotations needed)
In dynmust be specified: dyn Iterator<Item = u8>part of the type: dyn Codec<u8>

Rule: use an associated type when the type is an output determined by Self; use a generic parameter (with a default, trait Add<Rhs = Self>) when the same type sensibly implements the trait several times. Associated type bounds T: Iterator<Item: Display> are stable since 1.79.

Who May Implement It: Extensibility Patterns

PatternShapeUse when
Open traitpub trait Codec { .. }users must plug in their own types (serde, tower Service)
Sealed traitpub trait Codec: private::Sealedusers call it, only you implement it: free to add methods in minor releases (C-SEALED)
Extension traitpub trait StrExt { .. } impl StrExt for stradd methods to a foreign type (itertools::Itertools, futures::StreamExt)
Blanket-impl'd aliastrait ReadWrite: Read + Write {} impl<T: Read + Write> ReadWrite for T {}name a bound combination; users can never impl it manually (E0119)
Marker traitpub trait Pod: Copy + 'static {} (often unsafe trait)encode a property; unsafe trait when wrong impls cause UB
Newtypepub struct Meters(f64);implement foreign traits for foreign types (orphan rule), add invariants, hide representation (C-NEWTYPE)
rust
mod private {
    pub trait Sealed {}
}

/// Implemented for `Json` and `Toml` only; new methods are not breaking changes.
pub trait Format: private::Sealed {
    fn extension(&self) -> &'static str;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Json;

impl private::Sealed for Json {}
impl Format for Json {
    fn extension(&self) -> &'static str {
        "json"
    }
}

Coherence in one paragraph: impl ForeignTrait for ForeignType is E0117; impl<T> ForeignTrait for T is E0210 (uncovered type parameter); a local type must appear before any uncovered type parameter, so impl<T> From<Local> for Vec<T> and impl Mul<Vec2> for f64 are fine (Rust 1.41+). &T, &mut T, Box<T> and Pin<T> are fundamental: impl Display for Box<Local> is allowed. Two impls that could overlap for some type are E0119, including "upstream crates may add a new impl" cases. Details and fixes: coherence-and-trait-patterns.md.

async fn in Traits (Rust 1.75+)

SituationChoose
Private/internal trait, single-threaded or concrete typesplain async fn
Public trait, generic callers spawn on a multi-threaded runtime#[trait_variant::make(Name: Send)], or write fn f(&self) -> impl Future<Output = T> + Send
Need dyn Trait with async methods#[async_trait::async_trait] (boxes every call; Send by default, ?Send to opt out) or hand-written Pin<Box<dyn Future + Send + '_>>
Caller wants to require Send on one method's futurereturn-type notation T: Trait<method(..): Send> is not stable as of this writing; use a Send variant
rust
// Cargo.toml: trait-variant = "0.1"
#[trait_variant::make(Fetcher: Send)]
pub trait LocalFetcher {
    async fn fetch(&self, url: &str) -> Result<String, std::io::Error>;
}
// `Fetcher` is generated: same method, future is `+ Send`; impl Fetcher for your type and use
// `F: Fetcher + Clone + 'static` in code that tokio::spawn()s.

Core Rules

  • Implement From, never Into (Into comes from a blanket impl and ? uses From); TryFrom for fallible conversions; AsRef<T> for cheap reference views; Borrow<T> only when Eq/Hash/Ord agree (map keys).
  • Every public type: Debug (C-DEBUG). Then eagerly: Clone, Copy (small plain data only, removing it later is breaking), PartialEq/Eq, Hash, PartialOrd/Ord (when meaningful), Default, Display (user facing text), Send + Sync (check, do not assume), serde behind a serde cargo feature (C-SERDE).
  • Error types: an enum or struct with Display + Error + Send + Sync + 'static, #[non_exhaustive], source() wired (thiserror does all of it). Dynamic errors are Box<dyn Error + Send + Sync + 'static>.
  • Accept borrowed, general inputs (&[T], &str, &Path, impl AsRef<Path>, impl IntoIterator); return owned, concrete outputs. Take R: Read/W: Write by value (C-RW-VALUE; &mut R also implements them).
  • Bounds go on impls and functions, not on struct definitions (C-STRUCT-BOUNDS), except where the type's Drop or layout needs them.
  • Deref only for smart pointers and transparent wrappers (C-DEREF); never to fake inheritance.
  • Operators only with their mathematical meaning (C-OVERLOAD); implement the &a + &b forms for non-Copy types.
  • #[must_use] on builders and on functions whose only effect is their return value; #[non_exhaustive] on public enums/structs you expect to grow.
  • Callbacks: accept the weakest Fn trait you can (FnOnce > FnMut > Fn for caller freedom); store Box<dyn Fn(..) + Send + Sync>; async callbacks: AsyncFn traits (1.85+) or Fn() -> Fut.
  • A trait with one implementor and no dyn/test use is indirection: start concrete, extract a trait later.
  • Seal or #[doc(hidden)] anything you are not ready to promise; adding a required method to an open trait is a major release.

More: std-traits-and-api-guidelines.md, semver-and-evolution.md.

Symptom -> Cause -> Fix

SymptomCauseFix
E0038 the trait X is not dyn compatiblegeneric method, Self in signature, no receiver, async fn, Sized/Clone supertrait (TR001)where Self: Sized on those methods, box_clone, #[async_trait]
E0117 only traits defined in the current crate can be implemented for types defined outsideforeign trait for foreign typenewtype wrapper, or implement a local trait
E0210 type parameter T must be used as the type parameter for some local typeimpl<T> Display for T / impl<T> From<Local> for Tblanket impl a local trait, or impl for the concrete local types
E0119 conflicting implementationsoverlapping blanket impls, or impl<T> From<T> for Wrapper<T> vs core's impl<T> From<T> for Tnarrow the blanket impl, use a newtype or a named constructor
E0599 no method named x found / method exists but trait bounds were not satisfiedtrait not in scope, or a bound on the impl block is unmetuse crate::Ext;, add the bound to the caller, check derives (Clone/Debug on T)
E0277 the trait bound T: Send is not satisfied / future cannot be sent between threads safelyRc/RefCell/MutexGuard held across .await, or AFIT future not Send (TR008)Send variant (trait_variant), Arc/Mutex, drop guards before .await
E0191 the value of the associated type Item must be specifieddyn Iterator without Item = ..dyn Iterator<Item = T>
E0225 only auto traits can be used as additional traits in a trait objectdyn Read + Writesupertrait alias with blanket impl
E0283 type annotations neededgeneric param inferred from nothing (.into() with many From impls)turbofish / annotate, or associated type instead of parameter
E0392 parameter T is never usedtypestate / marker parameterPhantomData<T> (or PhantomData<fn() -> T> to stay Send/Sync and avoid drop-check)
E0204 the trait Copy cannot be implemented for this typea field is not Copyderive Clone only, or make the field Copy
E0782 trait objects must include the dyn keyword (edition 2021+)bare Box<Trait>Box<dyn Trait>
? does not convert into your erroronly impl Into<MyError> for E written (TR002)impl From<E> for MyError or #[from]
{:?} fails on a user's struct holding your typeyour public type lacks Debug (TR003)derive or hand-write a redacting Debug
Box<dyn Error> result cannot go into tokio::spawn / anyhowmissing Send + Sync (TR009)Box<dyn Error + Send + Sync + 'static>
method on wrapper silently calls the inner type's methodDeref used as inheritance (TR005)explicit delegation or a shared trait

Output Format

When reviewing, answer with:

  1. Summary - one paragraph: what the abstraction is for, who implements it, who calls it, and the verdict.
  2. Findings - ordered by severity; each with file:line, rule id (TRxxx when the script flags it, or the API guideline id such as C-DEBUG), why it matters for users or semver, and the concrete fix.
  3. Design decisions - dispatch choice, extensibility (open/sealed/extension), Send/Sync story, error type, each with the alternative considered and why it lost.
  4. Code - complete corrected trait + one impl + one call site (generic and/or dyn) that compiles on the stated MSRV; mark features needing a newer compiler (AFIT 1.75, upcasting 1.86, AsyncFn 1.85).
  5. Verification - cargo check, cargo clippy, the test that pins the property (e.g. a dyn usage or assert_send test, a compile_fail doctest), cargo semver-checks for published crates.

When designing from scratch, lead with the trait definition and a table of decisions, then the code.

Scripts

Small, standard-library-only Rust programs that read source text (no Cargo project, never compile your code). Build each once with rustc -O --edition 2021 scripts/<name>.rs -o /tmp/<name>.

ScriptRulesJob
scripts/dyn_compat.rsTR001, TR008traits used as dyn that break dyn compatibility (with every reason); public async fn traits without a Send story
scripts/trait_shape.rsTR002, TR005, TR006, TR007impl Into, Deref-as-inheritance, fat traits (--max-methods, default 8), private single-impl traits
scripts/api_traits.rsTR003, TR004, TR011, TR012, TR014std traits public types should have: Debug, Display + Error, Hash/PartialEq consistency, Default, #[non_exhaustive] errors
scripts/api_signatures.rsTR009, TR010, TR013Box<dyn Error> without Send + Sync, &Vec<T>/&String/&PathBuf params, builders without #[must_use]
scripts/scaffold_trait.rs-templates: sealed, extension, typestate, newtype, dyn-compatible, async-send (each compiles and passes the scanners)
scripts/rsparse.rs, scripts/cli.rs-shared modules: source scanner (traits, impls, attributes, test modules) and options/report
bash
/tmp/dyn_compat src/                                   # files and/or directories (skips target/, .git/)
/tmp/api_traits src/lib.rs --json                      # machine-readable findings with fix
cat src/lib.rs | /tmp/api_signatures -                 # stdin
/tmp/trait_shape src/ --strict --max-methods 6         # exit 1 on any finding (CI gate)
/tmp/api_traits src/ --select TR003,TR004 --ignore TR012
/tmp/dyn_compat --list-rules                           # each scanner lists its rules with why and fix
/tmp/scaffold_trait typestate --name Request           # also: sealed extension newtype dyn-compatible async-send
RuleNameSeverityFlags
TR001dyn-incompatible-traithightrait used as dyn with receiver-less, generic, impl Trait-taking, Self-using or async methods, consts, GATs or a Sized supertrait
TR002impl-into-not-frommediumimpl Into<X> for Y, impl TryInto
TR003missing-debugmediumpub struct/enum with no Debug derive or impl in the scanned files
TR004error-without-error-implhighpub *Error type lacking Display and/or std::error::Error (thiserror/snafu derives count)
TR005deref-inheritancemediumDeref<Target = LocalStruct> on a multi-field struct
TR006fat-traitlowpublic trait with more than --max-methods (default 8) required methods
TR007single-impl-traitlowprivate trait, one impl for a local type, never dyn or a bound
TR008async-fn-public-traitmediumasync fn in a pub trait without trait_variant/async_trait
TR009box-dyn-error-not-sendmediumBox<dyn Error> without Send in fns and type aliases (not fn main, not trait impls)
TR010borrowed-owned-paramlow&Vec<T>, &String, &Box<T>, &PathBuf, &OsString parameters (not in trait impls)
TR011hash-with-manual-eqmedium#[derive(Hash)] plus hand-written PartialEq
TR012new-without-defaultlowpub fn new() -> Self with no args and no Default
TR013builder-missing-must-uselowpub fn x(self, ..) -> Self methods without #[must_use] (method or type)
TR014error-enum-exhaustivelowpub enum *Error without #[non_exhaustive]

Scan the whole crate: impls in other files satisfy TR003/TR004 and dyn uses anywhere trigger TR001. #[cfg(test)] modules are skipped by the hygiene rules; silence a line with // noqa or // noqa: TR010. These are heuristic scanners, not rustc: macros, cfg variants and re-exports are invisible. Exit codes: 0 clean (or findings without --strict), 1 findings with --strict, 2 usage error or unreadable file.

Additional Resources

FileContent
dispatch-and-dyn-compatibility.mdGenerics vs impl Trait vs dyn, vtables, code size, every dyn-compatibility rule with E0038 fixes, upcasting, Any, dyn lifetimes
coherence-and-trait-patterns.mdOrphan rule (E0117/E0210), overlap (E0119), blanket impls, newtype, extension, sealed, marker and auto traits, supertraits, default methods, ?Sized, PhantomData
async-and-advanced-generics.mdasync fn in traits, the Send problem, trait_variant, async-trait, RPITIT, GATs, HRTB, Fn/AsyncFn traits, associated types vs params
std-traits-and-api-guidelines.mdStd trait checklist, From/Into/TryFrom/AsRef/Borrow, Display and Error, serde feature, Iterator/IntoIterator, operators, Deref, builders, typestate, must_use
semver-and-evolution.mdWhich trait/impl changes are major, minor or possibly-breaking; sealing, non_exhaustive, auto-trait leakage, cargo-semver-checks
testing-trait-designs.mdHand-written fakes, mockall, compile_fail doctests, trybuild, Send/Sync and dyn assertions, property tests for trait laws
version-history.mdTrait-related stabilisations by Rust version and edition, with MSRV guidance
More in Rust

rust-borrow-checker-error-explainer

Explain and fix Rust borrow-checker, ownership, move and lifetime compiler errors: E0382 use of moved value, E0499 cannot borrow as mutable more than once, E0502 also borrowed as immutable, E0505, E0506, E0503, E0507 cannot move out of index/Rc/reference, E0515 return reference to local, E0597 does not live long enough, E0716 temporary value dropped while borrowed, E0373 closure may outlive (spawn needs move), E0521 borrowed data escapes, E0384, E0596, E0594, E0381, E0713, 'lifetime may not live long enough', RefCell 'already borrowed: BorrowMutError' panics. Ranks fixes from restructuring (split borrows, entry API, split_at_mut, get_disjoint_mut, mem::take, Option::take, indices, thread::scope) down to clone and Rc<RefCell>/Arc<Mutex>; never unsafe. Covers NLL limits (get-or-insert), iterator invalidation, closure captures, self-referential structs. Ships std-only Rust triage tools.

rust-cargo-workspace-designer

Design, review and fix Cargo workspaces: when to split a crate (compile time, boundaries, publishing, proc-macros), crates/* vs apps/ + crates/ layouts, virtual manifest vs root package, members/exclude/default-members, resolver 2 vs 3 (MSRV-aware, edition 2024), [workspace.package] and [workspace.dependencies] inheritance, [workspace.lints], root-only profiles (lto, codegen-units), additive features with dep: and weak ?/ syntax, feature unification pitfalls, internal path + version deps, publish = false, Cargo.lock and MSRV policy, xtask, CI with --locked, nextest, rust-cache, sccache, cargo-hack/deny/machete/udeps/semver-checks, and cargo build --timings. Ships a workspace auditor and crate-graph tool.

rust-error-handling-thiserror-anyhow-designer

Design and review Rust error handling with thiserror 2 and anyhow 1: library vs application split, typed error enums with #[from], #[source], #[error(transparent)] and #[non_exhaustive], ErrorKind patterns, context via .context()/.with_context(), bail!/ensure!, source() chains, {:#} and {:?} reports, RUST_BACKTRACE/RUST_LIB_BACKTRACE, Box<dyn Error + Send + Sync>, main -> anyhow::Result or ExitCode, unwrap/expect/panic policy, Option to Result, collecting iterators of Results, axum IntoResponse mapping, retryable errors, tracing, async Send bounds, testing with matches! and downcast_ref, no_std core::error::Error, and snafu, eyre/color-eyre, miette, error-stack.

rust-lifetime-annotation-advisor

Decide when Rust lifetime annotations are needed and which ones: the three elision rules, structs holding references (struct Parser<'a>), owned vs borrowed (String, &str, Cow<'a, str>, Arc<str>), one vs several lifetimes, 'a: 'b bounds, T: 'static misconceptions, Box<dyn Trait + 'a> default object lifetimes, impl Trait + '_ and edition 2024 capture rules with use<..>, variance and PhantomData, HRTB for<'a> Fn(&'a T), GATs and lending iterators, async fn and tokio::spawn 'static, self-referential structs (ouroboros, self_cell, indices, arenas), Rc/Arc to escape lifetime soup. Ships std-only Rust checkers for redundant, missing, unused and over-restrictive lifetimes.

rust-tokio-async-service-builder

Build and review production async Rust services on Tokio 1.x and axum 0.8: runtime choice (#[tokio::main] multi_thread vs current_thread, Builder), axum routing with /{id} paths, State, extractors, IntoResponse error types, axum::serve with_graceful_shutdown, tower/tower-http middleware (TraceLayer, TimeoutLayer, CorsLayer, ConcurrencyLimitLayer, RequestBodyLimitLayer), Arc<AppState>, std vs tokio Mutex, JoinSet, TaskTracker, CancellationToken, select! cancel safety, bounded mpsc, oneshot, broadcast, watch, actors, Semaphore, timeouts, retries, spawn_blocking, sqlx PgPool, reqwest Client reuse, tracing, tests with start_paused and oneshot, Dockerfile. Fixes 'future cannot be sent between threads safely', E0373, Rc or MutexGuard held across await, 'Cannot start a runtime from within a runtime', 'Path segments must not start with :'. Ships std-only Rust scanners and a compiling axum 0.8 scaffold.

rust-unsafe-block-auditor

Audit unsafe Rust for soundness: inventory every unsafe block, unsafe fn, unsafe impl/trait, extern block and unsafe attribute, check // SAFETY: comments and # Safety docs, and classify each as sound, needs-justification or likely UB, proposing safe alternatives first. Covers the five unsafe superpowers, aliasing (Stacked/Tree Borrows), invalid values, MaybeUninit vs mem::zeroed/uninitialized, transmute, slice::from_raw_parts, Vec::set_len/from_raw_parts, get_unchecked, CString::as_ptr dangling, unsafe impl Send/Sync, Pin projection, FFI (extern "C", repr(C), C-unwind, Box::into_raw/from_raw), strict provenance, static mut and &raw, edition 2024 (unsafe extern, #[unsafe(no_mangle)], unsafe_op_in_unsafe_fn, env::set_var), Miri, sanitizers, cargo-fuzz, loom, cargo-geiger and clippy safety lints.