agentsclimarketplace

Rust api

Skill dawidpereira/rust-skills/skills/rust-api

Rust API design, naming conventions, and documentation standards. Use when designing public APIs, implementing builder patterns, choosing #[must_use]/#[non_exhaustive], following Rust naming conventions (as_/to_/into_ prefixes), or writing doc comments with examples. Also use for library design decisions like common trait implementations and serde feature gating.From its SKILL.md

Install
npx -y skills add dawidpereira/rust-skills --skill rust-api

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.7 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

API Design

Core Question

What does the caller need, and what should the compiler prevent?

Every API decision flows from this:

  • What is the minimum the caller must provide?
  • What mistakes can the type system catch at compile time?
  • What is the cost of each operation, and does the name communicate it?

If the caller can misuse your API without a compiler error, the API needs work.


API → Design Question

SymptomDon't Just SayAsk Instead
Constructor with 8 parameters"Use a builder"Which parameters are required vs optional?
Caller ignores return value"Add must_use"Is ignoring this value ever correct?
Adding enum variant breaks users"It's a breaking change"Should this enum be #[non_exhaustive]?
Method named get_name()"Remove the get_"Does this do more than return a field?
as_string() allocates"Rename to to_string()"What is the actual cost of this conversion?

Quick Decisions

ScenarioUseWhy
Many optional fields in constructorBuilder patternSelf-documenting, flexible
Required + optional fieldsTypestate builderCompiler enforces required fields
All fields have sensible defaults#[derive(Default)]Works with ..Default::default()
Return value must not be ignored#[must_use]Compiler warns on silent drop
Builder struct or method chain#[must_use] on type + methodsPrevents accidental drop
Public enum that may grow#[non_exhaustive]Add variants without breaking change
Public struct that may grow#[non_exhaustive] + constructorAdd fields without breaking change
Adding methods to external typesExtension trait (TypeExt)Works around orphan rules
Public type minimum traitsDebug, Clone, PartialEqBasic ecosystem interop
Serde in a library crateFeature flag, not hard depUsers who don't need it don't pay
Free reference conversionas_ prefixSignals O(1), no allocation
Allocating conversionto_ prefixSignals cost
Ownership-consuming conversioninto_ prefixSignals self is consumed
Simple field accessorNo get_ prefixname() not get_name()
Boolean-returning methodis_/has_/can_ prefixReads naturally in conditions

Builder Pattern

Choose the right builder variant based on your requirements:

Decision

VariantWhenbuild() returns
InfallibleAll fields have defaultsT
FallibleValidation can fail at runtimeResult<T, E>
TypestateRequired fields enforced at compile timeT
Consuming (mut self)Most common, simple chainingDepends
Borrowing (&mut self)Builder reused for multiple instancesDepends

Infallible Builder

#[derive(Default)]
#[must_use = "builders do nothing unless you call build()"]
pub struct WidgetBuilder {
    color: Option<Color>,
    size: Option<Size>,
}

impl WidgetBuilder {
    pub fn color(mut self, color: Color) -> Self {
        self.color = Some(color);
        self
    }

    pub fn build(self) -> Widget {
        Widget {
            color: self.color.unwrap_or(Color::Black),
            size: self.size.unwrap_or(Size::Medium),
        }
    }
}

Typestate Builder (compile-time required fields)

pub struct NoUrl;
pub struct HasUrl(String);

pub struct ClientBuilder<Url> {
    url: Url,
    timeout: Option<Duration>,
}

impl ClientBuilder<NoUrl> {
    pub fn new() -> Self {
        Self { url: NoUrl, timeout: None }
    }

    pub fn url(self, url: String) -> ClientBuilder<HasUrl> {
        ClientBuilder { url: HasUrl(url), timeout: self.timeout }
    }
}

impl ClientBuilder<HasUrl> {
    pub fn build(self) -> Client {
        Client { url: self.url.0, timeout: self.timeout }
    }
}

Common Traits Checklist

Derive these for every public type unless you have a reason not to:

Type CategoryDerive
Minimum (all public types)Debug, Clone, PartialEq
ID / key typesDebug, Clone, Copy, PartialEq, Eq, Hash
Small value typesDebug, Clone, Copy, PartialEq, Default
Config / optionsDebug, Clone, PartialEq, Default
Error typesDebug, Clone, PartialEq, Eq
HashMap keysAdd Eq, Hash
BTreeMap keysAdd Eq, Ord, PartialOrd
Serde support#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

Implement manually when derive does the wrong thing (e.g., case-insensitive equality, redacting sensitive fields in Debug).


Naming Quick Reference

Conversion Prefixes

PrefixCostOwnershipExample
as_Free O(1)&self -> &Uas_str(), as_bytes(), as_slice()
to_Allocates/computes&self -> Uto_string(), to_vec(), to_lowercase()
into_Consumes selfself -> Uinto_inner(), into_bytes(), into_vec()

Accessor Naming

PatternNameNot
Simple field accessname(), len()get_name(), get_len()
Fallible lookupget(), get_mut()find() (unless searching)
Boolean checkis_empty(), has_key(), can_write()empty(), key_exists()
Setterset_name(value)name(value) (unless builder)

Iterator Methods

MethodYieldsOwnership
iter()&TBorrows collection
iter_mut()&mut TMutably borrows
into_iter()TConsumes collection

Iterator type names match their method: iter() -> Iter, into_iter() -> IntoIter, keys() -> Keys.

General Rules

ElementConventionExampleNot
Types, traits, enumsUpperCamelCaseHttpServerHTTPServer
Enum variantsUpperCamelCaseNotFoundNOT_FOUND
Functions, methodssnake_caseparse_json()parseJSON()
Constants, staticsSCREAMING_SNAKE_CASEMAX_RETRIESmaxRetries
LifetimesShort lowercase'a, 'de, 'src'input_lifetime
Type paramsSingle uppercaseT, E, K, VElementType
AcronymsTreat as wordsUuid, HttpClientUUID, HTTPClient
Crate namesNo -rs/-rust suffixjson-parserjson-parser-rs

Usage Scenarios

Scenario 1: Designing a Library Config Type

You need a Config struct with 3 required fields and 5 optional fields.

  1. Use a builder with typestate for the 3 required fields
  2. Add #[must_use] to the builder type
  3. Derive Debug, Clone, PartialEq, Default on Config
  4. Add #[non_exhaustive] if the struct is public and may gain fields
  5. Gate serde behind a feature flag
  6. Document with # Examples showing builder usage

Scenario 2: Adding Conversion Methods to a Newtype

You have struct Email(String):

impl Email {
    /// Returns the email as a string slice. O(1), no allocation.
    pub fn as_str(&self) -> &str {
        &self.0
    }

    /// Returns a new lowercase version. Allocates.
    pub fn to_lowercase(&self) -> Email {
        Email(self.0.to_lowercase())
    }

    /// Consumes the Email, returning the inner String.
    pub fn into_string(self) -> String {
        self.0
    }

    pub fn is_valid(&self) -> bool {
        self.0.contains('@')
    }
}

Scenario 3: Extending an External Type

You need hex encoding for byte slices:

pub trait ByteSliceExt {
    fn as_hex(&self) -> String;
}

impl ByteSliceExt for [u8] {
    fn as_hex(&self) -> String {
        self.iter().map(|b| format!("{b:02x}")).collect()
    }
}

Import use my_crate::ByteSliceExt; to use. Name the trait with Ext suffix.


Reference Index

ReferenceRead When
api-patternsImplementing builders, choosing #[must_use]/#[non_exhaustive], extension traits, Default, common traits, serde gating
api-namingNaming methods, types, conversions, iterators, or crate names
api-documentationWriting doc comments, examples, error/panic/safety sections, intra-doc links, Cargo.toml metadata

Cross-References

NeedSkill
Error types for Result-returning APIsrust-errors
Newtype patterns, typestate, PhantomDatarust-types
Trait design, generics, dispatchrust-types
Testing doc examplesrust-quality
Clippy lints for API qualityrust-quality
Ownership decisions in API signaturesrust-ownership

What ships with it: 3 files

25.3 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.