SuperAPI AI Directory

Rust Ecosystem

Coding & development

View repository

Updated 10-16 05:52

About this skill

<rust_language> <ownership_borrowing> Each value has exactly one owner. When owner goes out of scope, value is dropped. Use move semantics by default; explicit Clone when needed

<concept name="borrowing">
  <description>Immutable and mutable references with strict rules</description>
  <rules priority="critical">
    <rule>&amp;T allows multiple simultaneous borrows</rule>
    <rule>&amp;mut T allows exactly one mutable borrow</rule>
    <rule>Cannot have &amp;mut T while &amp;T exists</rule>
  </rules>
</concept>

<concept name="lifetimes">
  <description>Lifetime annotations for reference validity</description>
  <pattern name="elision">
    <description>Compiler infers lifetimes in common patterns</description>
  </pattern>
  <pattern name="explicit">
    <description>Explicit lifetime annotations for complex cases</description>
    <example>
      fn foo&lt;'a>(x: &amp;'a str) -> &amp;'a str {
        x
      }
    </example>
  </pattern>
  <pattern name="static">
    <description>'static for values that live entire program</description>
  </pattern>
</concept>

</ownership_borrowing>

<pattern name="derive">
  <description>Automatically implement common traits</description>
  <example>
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    struct MyType {
      field1: String,
      field2: i32,
    }
  </example>
</pattern>

<error_handling> Recoverable errors with Result with T and E type parameters ? for early return on Err map, and_then, unwrap_or, unwrap_or_else <decision_tree name="when_to_use"> Is this a recoverable error that callers should handle? <if_yes>Return Result type with appropriate error variant</if_yes> <if_no>Use panic only for unrecoverable programming errors</if_no> </decision_tree>

<pattern name="Option">
  <description>Optional values with Option with T type parameter</description>
  <operators>? for early return on None</operators>
  <combinators>map, and_then, unwrap_or, unwrap_or_default</combinators>
</pattern>

<pattern name="custom_error">
  <description>Define custom error types with thiserror or anyhow</description>
  <example>
    #[derive(Debug, thiserror::Error)]
    enum MyError {
      #[error("IO error: {0}")]
      Io(#[from] std::io::Error),
      #[error("Parse error: {msg}")]
      Parse { msg: String },
    }
  </example>
  <note>Use thiserror 2.0+ which provides improved error messages and automatic From implementations.</note>
</pattern>

</error_handling>

<common_patterns> Fluent API for complex object construction MyStruct::builder() .field1(value1) .field2(value2) .build() <decision_tree name="when_to_use"> Does the struct have many optional fields or complex construction logic? <if_yes>Implement builder pattern for ergonomic construction</if_yes> <if_no>Use simple constructor function or Default trait</if_no> </decision_tree>

<pattern name="newtype">
  <description>Wrapper type for type safety</description>
  <example>
    struct UserId(u64);
  </example>
</pattern>

<pattern name="type_state">
  <description>Encode state in type system</description>
  <use_case>Prevent invalid state transitions at compile time</use_case>
</pattern>

</common_patterns>

<trust_boundary_types> A trust boundary is any point where data crosses from an unvalidated source (config files, external processes, network, shell) into code that acts on it. The general rule: never let a raw String or Map travel to the final action site (process spawn, shell emission, SQL) still typed as raw text. Wrap validated values in newtypes so the type system, not developer discipline, enforces that only validated data reaches the boundary.

<pattern name="validated_newtype_over_raw_string">
  <description>Public/config model fields that carry constrained values should use validated newtypes instead of raw String / HashMap&lt;String, String&gt;, so direct Rust construction cannot bypass the serde/runtime validators.</description>
  <rule>Do not add infallible From&lt;&amp;str&gt; or From&lt;String&gt; for a validated newtype — that reintroduces unchecked construction and defeats the boundary. Provide only a fallible constructor (TryFrom / new -&gt; Result).</rule>
  <example>
    // Raw form: any String can be constructed, validation is optional and easy to skip.
    struct Config { name: String }

    // Validated form: the only way to build a Name is through validation.
    struct Name(String);
    impl Name {
      fn new(s: &amp;str) -&gt; Result&lt;Self, NameError&gt; { /* enforce invariants */ }
    }
    struct Config { name: Name }
  </example>
</pattern>

<pattern name="single_source_for_schema_and_validator">
  <description>Derive the published schema and the runtime validator from the same constant. If a JSON Schema advertises an inclusive maximum while the runtime constructor enforces an exclusive one, a documented-valid config can be rejected at runtime (or worse, the reverse).</description>
  <rule>Keep schema generation, serde deserialization, and direct constructors on one typed boundary. Publish numeric bounds (e.g. exclusiveMaximum) from the same crate-level constant the constructor checks, not from a separately written field-level annotation.</rule>
</pattern>

<pattern name="runtime_validate_mirrors_serde">
  <description>When a type is constructed both via serde (deserialization) and directly by API callers, the direct-construction validate() path is a second trust boundary. It must mirror the serde validators exactly — same limits, same shared constants (e.g. a single MAX_DEPTH used by validator, compiler, and executor).</description>
  <rule>A separate model-side limit that disagrees with the downstream limit lets a "valid" value fail later during generation or execution. Route both paths through one constant.</rule>
</pattern>

<pattern name="convert_at_entry_require_at_boundary">
  <description>Keep serde-facing models ergonomic (plain String), but convert to the validated newtype immediately after validation, and require the newtype at every downstream API. Tests and real execution then share one un-bypassable boundary.</description>
  <rule>Downstream backend/executor signatures should take &amp;ValidatedName, not &amp;str, so no call site can smuggle an unvalidated value to the action point.</rule>
</pattern>

<pattern name="distinct_types_for_distinct_semantics">
  <description>When two values are both "strings" but have different execution semantics, give them distinct output types so call sites cannot mix them. A concrete argument value and an already-quoted shell word are not interchangeable even though both are text.</description>
  <rule>Cross the boundary explicitly (as_str() / into_inner()) so the conversion is visible and intentional. This also applies to identity handles parsed from external process stdout — parse into a validated Id type, then require that Id for follow-up operations.</rule>
</pattern>

<pattern name="reject_non_utf8_before_validation">
  <description>Bytes read from external processes are not guaranteed UTF-8. Reject non-UTF-8 before constructing an execution handle; lossy conversion (from_utf8_lossy / to_string_lossy) is acceptable only for user-facing diagnostics, never for values that will be fed back into execution.</description>
</pattern>

<pattern name="deterministic_ordering">
  <description>For maps whose iteration order feeds generated output (emitted options, template expansion, argv), use BTreeMap rather than HashMap at the public model type so ordering is deterministic at the type level. Downstream code then iterates directly instead of re-sorting at each call site.</description>
</pattern>

<pattern name="propagate_dont_silently_skip">
  <description>A loader that reads and validates a directory of config files is a trust boundary. It must not silently skip malformed files or directory-entry errors, because that makes validation/listing output disagree with actual on-disk state and can hide broken or unsafe definitions.</description>
  <rule>Ignore only explicitly out-of-scope inputs (e.g. unsupported extensions). Propagate parse/validation errors with the file path in the error chain, and propagate read_dir entry errors with directory context. Sort successful results for deterministic output.</rule>
</pattern>

</trust_boundary_types>

<pattern name="trait_plus_enum_dispatch">
  <description>For a closed set of implementers, combine a trait with an enum whose variants are the implementers, annotated with #[enum_dispatch]. This gives zero-cost (static) polymorphism — the enum forwards each trait method to the active variant with no Box&lt;dyn Trait&gt; indirection — while keeping one exhaustive registration point.</description>
  <example>
    #[enum_dispatch]
    trait Fetcher {
      async fn get_info(&amp;self, cl: &amp;Client) -&gt; PackageInfo;
    }

    #[enum_dispatch(Fetcher)]
    enum FetcherDispatch {
      FromGit(FromGit),
      FromRegistry(FromRegistry),
    }
  </example>
  <decision_tree name="when_to_use">
    <question>Is the set of implementers closed and known at compile time?</question>
    <if_yes>trait + enum_dispatch — static dispatch, exhaustive matching, no allocation</if_yes>
    <if_no>Box&lt;dyn Trait&gt; — open set, plugin boundaries, or heterogeneous collections whose members are decided at runtime</if_no>
  </decision_tree>
</pattern>

<pattern name="extension_procedure">
  <description>Formalize adding a new implementer so the enum stays the single source of truth.</description>
  <step order="1">Create the implementation module and implement the trait for the new type.</step>
  <step order="2">Add a variant to the dispatch enum (the enum carries the #[enum_dispatch(Trait)] attribute).</step>
  <step order="3">Wire construction/selection (CLI variant, factory, or detection logic) to produce the new variant.</step>
  <note>Because dispatch is exhaustive over the enum, the compiler flags every match that must handle the new variant — the type system drives completeness.</note>
</pattern>

<edition_2024_features> Rust now supports async closures like async || {} which return futures when called (Edition 2024). let closure = async || { do_async_work().await }; let result = closure().await; impl Trait return types now capture all in-scope lifetimes by default. Use use<..> to explicitly specify captured parameters. #[diagnostic::do_not_recommend] attribute lets crate authors control which trait implementations are suggested in compiler diagnostics. Changed scope of temporaries for if let expressions and tail expressions in blocks (Edition 2024). async fn in trait definitions and implementations is stable since Rust 1.75. No longer requires the async-trait crate for most use cases. trait DataStore { async fn fetch(&self, id: u64) -> Result<Data, Error>; }

    impl DataStore for PostgresStore {
      async fn fetch(&amp;self, id: u64) -&gt; Result&lt;Data, Error&gt; {
        sqlx::query_as("SELECT * FROM data WHERE id = $1")
          .bind(id)
          .fetch_one(&amp;self.pool)
          .await
      }
    }
  </example>
  <note>For public traits in libraries, consider using trait_variant::make to provide Send bounds.</note>
</feature>
<feature name="let_chains">
  <description>Chain let patterns in if and while conditions (stable in edition 2024).</description>
  <example>
    if let Some(user) = get_user(id)
      &amp;&amp; let Some(email) = user.email
      &amp;&amp; email.ends_with("@company.com")
    {
      send_internal_notification(email);
    }
  </example>
</feature>

</edition_2024_features>

<anti_patterns> Using unwrap() in library code Use ? or proper error handling instead

<avoid name="clone_abuse">
  <description>Cloning values unnecessarily</description>
  <instead>Prefer borrowing when possible</instead>
</avoid>

<avoid name="string_for_everything">
  <description>Using String for all domain values</description>
  <instead>Use enums, newtypes for domain modeling</instead>
</avoid>

<avoid name="arc_mutex_overuse">
  <description>Defaulting to Arc with Mutex with T for concurrency</description>
  <instead>Consider channels or ownership patterns first</instead>
</avoid>

</anti_patterns> </rust_language>

<module_organization>
  <pattern name="mod_rs">
    <description>src/module/mod.rs with submodules</description>
  </pattern>
  <pattern name="file_module">
    <description>src/module.rs (preferred for simple modules)</description>
  </pattern>
</module_organization>

</project_structure>

<cargo_toml> <basic_structure> [package] name = "my-crate" version = "0.1.0" edition = "2024" # Current edition (stable since Rust 1.85) rust-version = "1.85" # Minimum supported version for edition 2024

  [dependencies]
  serde = { version = "1.0", features = ["derive"] }

  [dev-dependencies]
  tokio-test = "0.4"

  [build-dependencies]
  cc = "1.0"
</basic_structure>

<feature_flags>
  [features]
  default = ["std"]
  std = []
  async = ["tokio"]
  full = ["std", "async"]
</feature_flags>

<profile_optimization>
  [profile.release]
  lto = true
  codegen-units = 1
  panic = "abort"
  strip = true

  [profile.dev]
  opt-level = 0
  debug = true
</profile_optimization>

</cargo_toml>

  [workspace.package]
  version = "0.1.0"
  edition = "2024"
  license = "MIT"

  [workspace.dependencies]
  serde = "1.0"
  tokio = { version = "1", features = ["full"] }
</root_cargo_toml>

<member_inheritance>
  [package]
  name = "crate-a"
  version.workspace = true
  edition.workspace = true

  [dependencies]
  serde.workspace = true
</member_inheritance>

<decision_tree name="when_to_use">
  <question>Do you have multiple related crates in one repository?</question>
  <if_yes>Use workspace to share dependencies and build configuration</if_yes>
  <if_no>Single crate project without workspace structure</if_no>
</decision_tree>
<configuration>
  <file_reference>In Cargo.toml</file_reference>
  [lints.clippy]
  pedantic = "warn"
  nursery = "warn"
  unwrap_used = "deny"
  expect_used = "deny"

  <file_reference>Or in clippy.toml</file_reference>

  msrv = "1.94"
  cognitive-complexity-threshold = 25
</configuration>

<common_lints>
  <lint name="clippy::unwrap_used">Prefer ? or proper error handling</lint>
  <lint name="clippy::expect_used">Prefer ? or proper error handling</lint>
  <lint name="clippy::pedantic">Stricter lints for cleaner code</lint>
  <lint name="clippy::nursery">Experimental but useful lints</lint>
</common_lints>
<configuration>
  <file_reference>rustfmt.toml</file_reference>
  edition = "2024"
  max_width = 100
  use_small_heuristics = "Max"
  imports_granularity = "Crate"
  group_imports = "StdExternalCrate"
  reorder_imports = true
</configuration>

<cargo_nextest> Next-generation test runner with better output and parallelism cargo nextest run

<features>
  <feature>Parallel test execution</feature>
  <feature>Better failure output</feature>
  <feature>JUnit XML output for CI</feature>
  <feature>Test retries</feature>
</features>

<configuration>
  <file_reference>.config/nextest.toml</file_reference>
  [profile.default]
  retries = 2
  slow-timeout = { period = "60s", terminate-after = 2 }
  fail-fast = false
</configuration>

</cargo_nextest>

<other_tools> Security vulnerability scanning Dependency license and security checks Check for outdated dependencies Auto-rebuild on file changes Macro expansion debugging Install pre-built binaries from crates.io (faster than cargo install) Supply chain security — audit third-party crate reviews Mutation testing to verify test effectiveness </other_tools>

<release_profile_and_test_gating> Assertions and tests behave differently under the release profile than under dev. debug_assert! and any debug-only invariant check compile out when built with --release, so tests that expect a debug_assert to fire will not observe it in an optimized build. Packaging pipelines (distro packages, sandboxed builds) commonly build with --release and no network, so the release build is the one that actually ships — design tests to stay green there.

<context7_integration> Use Context7 MCP for up-to-date Rust documentation

<rust_libraries> </rust_libraries>

<usage_patterns> resolve-library-id libraryName="rust lang" Workflow guidance Step completed get-library-docs context7CompatibleLibraryID="/rust-lang/book" topic="ownership" Workflow guidance Step completed

<pattern name="cargo_configuration">
  <step order="1">

get-library-docs context7CompatibleLibraryID="/rust-lang/cargo.git" topic="workspace" Workflow guidance Step completed

<pattern name="clippy_lints">
  <step order="1">

get-library-docs context7CompatibleLibraryID="/rust-lang/rust-clippy" topic="lints configuration" Workflow guidance Step completed </usage_patterns> </context7_integration>

<best_practices> Use cargo check for fast iteration during development Run cargo clippy before committing Format with cargo fmt for consistent style Use workspace for multi-crate projects Prefer &str over String for function parameters Use impl Trait for return types when possible Document public API with /// doc comments Write unit tests alongside code in same file Use integration tests in tests/ for API testing Set rust-version in Cargo.toml for MSRV </best_practices>

<error_escalation inherits="core-patterns#error_escalation"> Clippy warning about style Borrow checker error Breaking change in public API Unsafe code without proper justification </error_escalation>

<related_skills> Navigate trait implementations and module hierarchies Fetch Rust book, cargo, and clippy documentation Debug borrow checker errors, lifetime issues, and performance bottlenecks </related_skills> <related_agents> Locate code patterns and references in this skill domain Review implementation quality against this skill guidance Analyze code complexity and suggest refactoring improvements </related_agents>