Rust Trait API Design
Use this skill to make Rust APIs generic where useful, concrete where simpler,
and object-safe when dynamic dispatch is part of the design. Optimize for the
smallest capability contract that callers and implementors can understand.
Core Workflow
- Identify whether the API consumes, borrows, returns, stores, or dispatches
behavior.
- Start with concrete types for local code. Generalize only when at least two
callers or implementations need the flexibility.
- Use generic bounds for compile-time polymorphism and inlining. Use
dyn Trait for heterogeneous values, plugin-like extension points, or runtime
dispatch.
- Place bounds at the point that needs them. Prefer
where clauses when
bounds are long or involve associated types.
- Decide whether a trait is intended for downstream implementation. Seal it,
keep fields private, or use constructors when invariants must not be
implemented externally.
- Use standard conversion traits where they match exactly. Do not invent a
custom conversion trait before checking
From, TryFrom, AsRef, AsMut,
Borrow, ToOwned, and Cow.
- Add compile tests, unit tests, or examples that prove the public API is
callable the way the skill expects future users to call it.
API Design Rules
- Accept
impl Trait or a named generic for parameters when callers should
pass many concrete types.
- Return
impl Trait when hiding one concrete return type. Return Box<dyn Trait> when the concrete type varies at runtime.
- Prefer associated types when each implementation has one natural related
type. Prefer generic trait parameters when one implementation supports many
target types.
- Implement
From for infallible conversions and TryFrom for fallible
conversions. Implementing these gives callers Into and TryInto.
- Use
AsRef for cheap reference-to-reference conversion. Use Borrow only
when borrowed and owned forms have equivalent Eq, Hash, and Ord
behavior.
- Avoid
Copy bounds unless the algorithm semantically requires bitwise copy.
Trait Object Review
Read references/trait-api-patterns.md when choosing between generics and
dyn Trait, or when public traits fail object-safety checks.
Before making a public trait object-safe, check:
- Methods do not use generic type parameters.
- Methods do not return
Self unless constrained with where Self: Sized.
- Associated types are specified on the trait object where needed.
- The object is behind a pointer such as
&dyn Trait, Box<dyn Trait>, or
Arc<dyn Trait + Send + Sync>.
Common Smells
- A public function takes
&Vec<T> because it only needs iteration.
- The API accepts
String and immediately borrows it as &str.
- The API implements
Into directly instead of From.
- A trait has many blanket bounds that only one method needs.
- A trait object was used because lifetimes were confusing, not because runtime
dispatch is required.
async-trait is used in a public trait without checking whether native
async fn in traits, trait-variant, or boxed futures fit the dispatch and
Send needs better.
Source: hashgraph-online/awesome-codex-plugins → plugins/LVTD-LLC/skills/skills/rust-trait-api-design/SKILL.md
1---2name: rust-trait-api-design3description: Design and review Rust trait APIs, generic bounds, dispatch models, and conversion contracts for clear public interfaces. Use when designing, refactoring, or reviewing traits, generics, trait objects, associated types, impl Trait, From/TryFrom, AsRef, Borrow, or crate APIs.4---567# Rust Trait API Design89Use this skill to make Rust APIs generic where useful, concrete where simpler,10and object-safe when dynamic dispatch is part of the design. Optimize for the11smallest capability contract that callers and implementors can understand.1213## Core Workflow14151. Identify whether the API consumes, borrows, returns, stores, or dispatches16 behavior.172. Start with concrete types for local code. Generalize only when at least two18 callers or implementations need the flexibility.193. Use generic bounds for compile-time polymorphism and inlining. Use `dyn20 Trait` for heterogeneous values, plugin-like extension points, or runtime21 dispatch.224. Place bounds at the point that needs them. Prefer `where` clauses when23 bounds are long or involve associated types.245. Decide whether a trait is intended for downstream implementation. Seal it,25 keep fields private, or use constructors when invariants must not be26 implemented externally.276. Use standard conversion traits where they match exactly. Do not invent a28 custom conversion trait before checking `From`, `TryFrom`, `AsRef`, `AsMut`,29 `Borrow`, `ToOwned`, and `Cow`.307. Add compile tests, unit tests, or examples that prove the public API is31 callable the way the skill expects future users to call it.3233## API Design Rules3435- Accept `impl Trait` or a named generic for parameters when callers should36 pass many concrete types.37- Return `impl Trait` when hiding one concrete return type. Return `Box<dyn38 Trait>` when the concrete type varies at runtime.39- Prefer associated types when each implementation has one natural related40 type. Prefer generic trait parameters when one implementation supports many41 target types.42- Implement `From` for infallible conversions and `TryFrom` for fallible43 conversions. Implementing these gives callers `Into` and `TryInto`.44- Use `AsRef` for cheap reference-to-reference conversion. Use `Borrow` only45 when borrowed and owned forms have equivalent `Eq`, `Hash`, and `Ord`46 behavior.47- Avoid `Copy` bounds unless the algorithm semantically requires bitwise copy.4849## Trait Object Review5051Read `references/trait-api-patterns.md` when choosing between generics and52`dyn Trait`, or when public traits fail object-safety checks.5354Before making a public trait object-safe, check:5556- Methods do not use generic type parameters.57- Methods do not return `Self` unless constrained with `where Self: Sized`.58- Associated types are specified on the trait object where needed.59- The object is behind a pointer such as `&dyn Trait`, `Box<dyn Trait>`, or60 `Arc<dyn Trait + Send + Sync>`.6162## Common Smells6364- A public function takes `&Vec<T>` because it only needs iteration.65- The API accepts `String` and immediately borrows it as `&str`.66- The API implements `Into` directly instead of `From`.67- A trait has many blanket bounds that only one method needs.68- A trait object was used because lifetimes were confusing, not because runtime69 dispatch is required.70- `async-trait` is used in a public trait without checking whether native71 `async fn` in traits, `trait-variant`, or boxed futures fit the dispatch and72 `Send` needs better.7374---7576**Source:** [`hashgraph-online/awesome-codex-plugins`](https://github.com/hashgraph-online/awesome-codex-plugins) → `plugins/LVTD-LLC/skills/skills/rust-trait-api-design/SKILL.md`