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.

Ir a la instalación

Datos de origen

Repositorio
full-stack-skills/rust-skills
Última actividad en el origen
11 de septiembre de 2026 a las 13:43
Idioma detectado de SKILL.md
inglés
Estrellas
5
Forks
1

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
9 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
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! {
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub