Skip to main content

rust-api-design

Design Rust library APIs that follow the Rust API Guidelines — naming (C-CASE, C-CONV, C-GETTER), interop traits (C-COMMON-TRAITS, C-CONVERT, C-ITER, C-SERDE), predictability (C-INTUITIVE, C-CONST), flexibility (C-GENERIC, C-NEWTYPE, C-EXT), type safety (C-BOOL, C-NONZERO, C-WRAPPER, C-STR), dependability (C-PANIC, C-UNWRAP), debuggability (C-DEBUG), and future-proofing (C-SEALED, C-STRUCT-FIELD, C-NON-EXHAUSTIVE). Use when users design a public crate API, choose between generics/concrete/newtype, decide trait bounds, hide implementation, avoid breakage, or ask "what is idiomatic Rust API design"; hand semver and publish workflow to rust-semver, lint config to rust-style-clippy, and in-crate layout to rust-module-layout.

Zur Installation springen

Quellinformationen

Repository
full-stack-skills/rust-skills
Letzte Quellaktivität
11. September 2026 um 13:43
Erkannte Sprache von SKILL.md
Englisch
Sterne
5
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
9 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
rust-api-design
license
Apache-2.0
description
Design Rust library APIs that follow the Rust API Guidelines — naming (C-CASE, C-CONV, C-GETTER), interop traits (C-COMMON-TRAITS, C-CONVERT, C-ITER, C-SERDE), predictability (C-INTUITIVE, C-CONST), flexibility (C-GENERIC, C-NEWTYPE, C-EXT), type safety (C-BOOL, C-NONZERO, C-WRAPPER, C-STR), dependability (C-PANIC, C-UNWRAP), debuggability (C-DEBUG), and future-proofing (C-SEALED, C-STRUCT-FIELD, C-NON-EXHAUSTIVE). Use when users design a public crate API, choose between generics/concrete/newtype, decide trait bounds, hide implementation, avoid breakage, or ask "what is idiomatic Rust API design"; hand semver and publish workflow to rust-semver, lint config to rust-style-clippy, and in-crate layout to rust-module-layout.
# Rust API Design (Rust API Guidelines) > Authority: [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/about.html) — the de-facto standard checklist of ~100 C-* rules used by std, tokio, serde, bevy. See [the full checklist](references/api-guidelines-checklist.md) for canonical wording of every rule. This skill owns the **shape of a public Rust API**: types, traits, naming, conversions, and the boundaries that keep the API usable, ergonomic, and resistant to breakage. It does **not** own Cargo manifest (`rust-cargo-build`), workspace topology (`rust-workspace`), in-crate `src/` layout (`rust-module-layout`), or semver/publish (`rust-semver`). ## Capability Boundaries ### ✅ Strengths 1. Translating API Guidelines' ~100 C-* rules into concrete design decisions for a public crate 2. Naming types, functions, methods, and features to match std and ecosystem conventions 3. Choosing between generics, trait objects, concrete types, and the newtype pattern 4. Designing `From`/`Into`/`TryFrom`/`AsRef`/`Borrow` conversions (rejecting `Deref` polymorphism) 5. Picking which auto traits (`Debug`, `Clone`, `Eq`, `Hash`, `Send`, `Sync`) to derive 6. Sealing traits, marking `#[non_exhaustive]`, hiding struct fields — to reserve room to evolve 7. Replacing bool parameters with enums, raw integers with newtypes, `String` with `&str` 8. Designing iterators, `Extend`, `Default`, `Display`/`FromStr` correctly 9. Avoiding `panic!`/`unwrap`/`expect` in public APIs in favor of `Result` ### ⚠️ Prerequisites 1. Rust ownership, traits, lifetimes — see `rust-stable` 2. Module visibility and re-exports — see `rust-module-layout` 3. Cargo manifest for `[features]`, `optional = true` — see `rust-cargo-build` ### ❌ Out of Scope 1. Semver rules and publish workflow → `rust-semver` 2. Clippy lint configuration → `rust-style-clippy` 3. Doctests and rustdoc comments → `rust-documentation` 4. Workspace topology → `rust-workspace` ## Data Privacy This skill does not collect, store, or transmit user data. --- # The Eight Chapters (One Section Per API Guidelines Chapter) The Guidelines are organized into 11 chapters. This skill owns 8 — the design chapters. Documentation, Macros, and Necessities live in `rust-documentation`, `rust-macros`, and `rust-cargo-build` respectively. ## 1. Naming (C-CASE, C-CONV, C-GETTER, C-NAMING) ### C-CASE — casing conventions | Item | Case | Example | |------|------|---------| | Types (struct/enum/trait), modules, crates | `UpperCamelCase` | `HttpClient`, `tokio` | | Functions, methods, locals, fields | `snake_case` | `send_request` | | Constants, statics | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES` | | Generic types | single `UpperCamelCase` letter or short word | `T`, `K`, `V`, `Req` | | Lifetimes | short `'a`/`'b` or descriptive `'src` | `'src` | | Features | `kebab-case` (Cargo enforces) | `serde-json` | Avoid ad-hoc abbreviations: `BufferedReader` is std-compliant; `BufRdr` is not. ### C-CONV — conversion method naming | Conversion | Prefix | Borrows? | Example | |-----------|--------|----------|---------| | Cheap, borrowed | `as_` | Yes (`&self → &T`) | `as_slice`, `as_bytes` | | Cheap, owned | `to_` (no alloc) / `into_` (consumes self) | Varies | `to_vec`, `into_bytes` | | Expensive, returns new | `to_` | No (`&self → T`) | `to_lowercase` | | Consuming | `into_` | No (`self → T`) | `into_string`, `into_iter` | | Fallible | `TryFrom`/`TryInto` | — | `u32::try_from(byte)` | Requirements: **(1)** A method named `as_X` that returns an owned `X` is wrong — rename to `to_X`. **(2)** The matched inverse for mutable borrows is `as_X_mut` / `X_mut`. ### C-GETTER — accessor naming ```rust // ✅ C-GETTER compliant pub struct Buffer { data: Vec<u8> } impl Buffer { pub fn data(&self) -> &[u8] { &self.data } // no get_ prefix pub fn data_mut(&mut self) -> &mut [u8] { &mut self.data } pub fn len(&self) -> usize { self.data.len() } // not get_len pub fn is_empty(&self) -> bool { self.data.is_empty() } } ``` Exceptions where `get_` is allowed: `Cell::get`, `Map::get` (genuine lookup semantics). ## 2. Interoperability (C-COMMON-TRAITS, C-CONVERT, C-ITER, C-SERDE) ### C-COMMON-TRAITS — derive the obvious traits For every public type, ask: should this derive `Debug`, `Clone`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash`, `Default`? | Trait | Default | Exception | |-------|---------|-----------| | `Debug` | Yes | Secrets → manual `Debug` that redacts | | `Clone` | Yes if cheap | Expensive clone → omit + document | | `Copy` | Only ≤16 bytes + lossless | — | | `PartialEq`/`Eq` | Yes if total order | `f64` cannot be `Eq` | | `Hash` | Yes if `Eq` | Must agree with `Eq` | | `Default` | Yes if natural empty/zero | — | ### C-CONVERT — `From`/`Into`/`AsRef`/`Borrow`; reject `Deref` polymorphism ```rust // ✅ Impl From<T> for U; Into comes for free impl From<ErrorKind> for Error { fn from(k: ErrorKind) -> Self { Error::Kind(k) } } // ✅ AsRef for borrowed views impl AsRef<str> for Name { fn as_ref(&self) -> &str { &self.0 } } // ✅ Borrow<str> if Eq/Hash should agree with str impl Borrow<str> for Name { fn borrow(&self) -> &str { &self.0 } } ``` **Reject `Deref` as polymorphism**: `Deref` is for smart pointers (`Box`, `Rc`, `Arc`, `String` — std precedent). Using `Deref` to make `MyClient` transparent to `HttpClient` gives hidden method injection, breaks `&self` resolution, and is the **deref polymorphism anti-pattern**. Use `AsRef`, an explicit method, or composition. ### C-ITER — iterator design ```rust // ✅ Provide IntoIterator for &T, &mut T, T when sensible impl<'a> IntoIterator for &'a Grid { type Item = &'a Cell; type IntoIter = std::slice::Iter<'a, Cell>; fn into_iter(self) -> Self::IntoIter { self.cells.iter() } } impl Grid { pub fn iter(&self) -> impl Iterator<Item = &Cell> { /* */ } pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Cell> { /* */ } } impl Extend<Cell> for Grid { /* */ } ``` Rules: `iter()` borrows, `iter_mut()` mutably borrows, `into_iter()` consumes. Never return `Vec` from iteration methods — return `impl Iterator`. ### C-SERDE — serialization interop - Use `#[serde(rename_all = "kebab-case")]` consistently within a type - `#[non_exhaustive]` enums need `#[serde(other)]` for forward compat with unknown variants - Re-export serde under a feature (`serde = ["dep:serde"]`) so downstream can opt out ## 3. Predictability (C-INTUITIVE, C-CONST, C-COMMON) ### C-INTUITIVE — naming reflects semantics If the caller had to read the source to know what `read` does, the API is wrong. Don't surprise: a `read` method that panics on EOF is a defect. ### C-CONST — `const fn` where possible ```rust pub const fn new(value: u32) -> Self { Self(value) } const fn is_power_of_two(n: u32) -> bool { n != 0 && n & (n - 1) == 0 } ``` Every std lib API that can be `const fn` is a candidate. Enables `const MAX: UserId = UserId::new(1000);` in callers. ### C-COMMON — sensible defaults via `Default` and builder ```rust impl Default for Config { fn default() -> Self { Config { retry_count: 3, timeout: Duration::from_secs(30) } } } // Builder for complex construction let client = Client::builder().with_retry(3).with_timeout(Duration::from_secs(10)).build()?; ``` ## 4. Flexibility (C-OVERLOAD, C-GENERIC, C-NEWTYPE, C-EXT) ### C-GENERIC — generics on input, concrete on output ```rust // ✅ Generic over AsRef<str> — caller passes &str, String, Cow pub fn parse(input: impl AsRef<str>) -> Result<Foo> { /* */ } // ❌ Concrete &str — forces caller to borrow pub fn parse(input: &str) -> Result<Foo> { /* */ } ``` Trade-off: more generics → longer compile, harder diagnostics. Generic on input types (`AsRef<str>`, `IntoIterator`); concrete on output. ### C-NEWTYPE — wrap primitives to prevent misuse ```rust // ✅ Newtypes around raw primitives pub struct UserId(pub u64); pub struct Email(String); // private inner — can't be constructed unsafely fn delete_user(id: UserId) { /* */ } // can't accidentally pass a PostId // ❌ Plain primitives — confusion and argument-order bugs fn delete_user(id: u64) { /* */ } ``` Zero-cost (compile to the underlying type), prevent argument-order bugs, and allow attaching methods (`UserId::is_anonymous()`). ### C-EXT — extension traits via `Ext` suffix ```rust pub trait StringExt { fn slugify(&self) -> String; } impl StringExt for str { fn slugify(&self) -> String { /* */ } } // Caller opts in: use my_crate::StringExt; "Hello World".slugify(); ``` Don't put methods directly on `String`/`Vec`/`HttpRequest` from other crates — use an `Ext` trait. ## 5. Type Safety (C-BOOL, C-NONZERO, C-STR, C-SIGNED, C-BITFLAG, C-WRAPPER, C-INTERVAL) > Authority: [API Guidelines — Type Safety](https://rust-lang.github.io/api-guidelines/type-safety.html). See `references/api-guidelines-checklist.md` for canonical wording. ### C-BOOL — replace bool parameters with enums ```rust // ✅ Enum — caller intent is explicit at call site pub enum Trim { Whitespace, None } pub fn parse(input: &str, trim: Trim) -> Result<Foo> { /* */ } parse(" x ", Trim::Whitespace); // ❌ Bool — caller must remember what true means pub fn parse(input: &str, trim: bool) -> Result<Foo> { /* */ } parse(" x ", true); // true = ?? ``` Two bool params compound: `f(true, false, true)` is incomprehensible. Two-arg enums are the floor. Set `clippy.toml` `max-fn-params-bools = 1` and `max-struct-bools = 1` to enforce mechanically. ### C-NONZERO — `NonZeroUsize` when zero is invalid ```rust use std::num::NonZeroUsize; // ✅ NonZeroUsize encodes "≥ 1" in the type pub fn chunk_size(&self) -> NonZeroUsize { /* */ } ``` Enables niche optimization: `Option<NonZeroU32>` is the same size as `u32`. Use `NonZeroU8`/`NonZeroU16`/`NonZeroU32`/`NonZeroU64`/`NonZeroUsize` and the `NonZeroI*` variants. ### C-STR — `&str` not `&String`; `&[T]` not `&Vec<T>` ```rust // ✅ Borrow slices for inputs pub fn process(data: &[u8], name: &str) { /* */ } // ❌ Forces caller to have owned collections pub fn process(data: &Vec<u8>, name: &String) { /* */ } ``` ### C-SIGNED — prefer unsigned types when values can't be negative ```rust // ✅ u64 — semantically "count" can't be negative pub struct Counter { count: u64 } // ❌ i64 — implies negative values are valid (they aren't) pub struct Counter { count: i64 } ``` For special ranges (e.g., `Age` 0..=150), use a newtype with validating constructor — let the type system prevent invalid values. ### C-BITFLAG — use the `bitflags!` macro for flag sets ```rust use bitflags::bitflags; bitflags! {
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen