feat(add rust skill):

Amolith created

Change summary

skills/writing-rust/SKILL.md             | 147 ++++++++++++++++++++++++++
skills/writing-rust/references/idioms.md | 147 ++++++++++++++++++++++++++
2 files changed, 294 insertions(+)

Detailed changes

skills/writing-rust/SKILL.md 🔗

@@ -0,0 +1,147 @@
+---
+name: writing-rust
+description: Writes idiomatic, well-structured Rust code following community conventions and best practices. Use when working on Rust projects, writing Rust code, debugging Rust errors, or when the user mentions Rust, Cargo, or crates.
+license: AGPL-3.0-or-later
+metadata:
+  author: Amolith <amolith@secluded.site>
+---
+
+Write idiomatic Rust. All code must compile, pass clippy, and have tests.
+
+## Code quality checks
+
+Run these in order before considering any code complete. Never submit code that fails any check.
+
+```bash
+cargo fmt
+cargo check
+cargo clippy -- -D warnings
+cargo test
+```
+
+## Project structure
+
+```
+project-root/
+├── Cargo.toml
+├── Cargo.lock
+├── src/
+│   ├── main.rs              # Binary entry point (if applicable)
+│   ├── lib.rs               # Library root (if applicable)
+│   └── module_name/
+│       ├── mod.rs
+│       └── submodule.rs
+├── tests/                   # Integration tests
+├── benches/                 # Benchmarks (if applicable)
+└── examples/                # Example usage (if applicable)
+```
+
+## Naming conventions
+
+| Item             | Convention        | Example                    |
+|------------------|-------------------|----------------------------|
+| Crates           | `snake_case`      | `my_crate`                 |
+| Modules          | `snake_case`      | `my_module`                |
+| Types / Traits   | `PascalCase`      | `MyStruct`, `MyTrait`      |
+| Functions        | `snake_case`      | `do_something()`           |
+| Constants        | `SCREAMING_SNAKE` | `MAX_RETRIES`              |
+| Local variables  | `snake_case`      | `item_count`               |
+| Type parameters  | Single uppercase  | `T`, `E`, `K`, `V`        |
+| Lifetimes        | Short lowercase   | `'a`, `'de`                |
+
+## Error handling
+
+- Prefer `Result<T, E>` over `panic!()` for recoverable errors.
+- Use `thiserror` for library error types, `anyhow` for application-level errors.
+- Avoid `.unwrap()` and `.expect()` in production code. Propagate with `?`.
+- Define custom error types when a module has more than one failure mode.
+- When a fallible function consumes an argument, return it inside the error type so callers can recover it.
+
+## Ownership and borrowing
+
+- Prefer borrowing (`&T`, `&mut T`) over transferring ownership when the caller doesn't need to give up the value.
+- Use `Clone` sparingly — never just to satisfy the borrow checker.
+- Prefer `&str` over `String`, `&[T]` over `&Vec<T>`, and `&T` over `&Box<T>` in function parameters.
+- Use `Cow<'_, str>` when a function may or may not need to allocate.
+- Use `mem::take` or `mem::replace` to move values out of mutable references without cloning.
+
+## Structs and enums
+
+- Derive common traits where appropriate: `Debug`, `Clone`, `PartialEq`, `Eq`, `Hash`, `Default`.
+- Use the builder pattern for structs with many optional fields.
+- Prefer enums over boolean flags for state representation.
+- Use `#[non_exhaustive]` on public structs and enums that may grow over time.
+- Use the newtype pattern for type safety wrappers around primitives (zero-cost abstraction).
+- Decompose large structs into smaller ones when the borrow checker prevents independent field access.
+
+## Testing
+
+- All implemented functionality must have corresponding tests.
+- Tests must validate behavior, not just assert `true`.
+- Use descriptive test names that explain what is being tested.
+- Test both the happy path and error/edge cases.
+
+```bash
+cargo test                  # Run all tests
+cargo test test_name        # Run a specific test
+cargo test -- --nocapture   # Run tests with output
+```
+
+## Design principles
+
+- **KISS**: Simplicity should be a key goal in design.
+- **YAGNI**: Don't add functionality until it is necessary.
+- **DRY**: Every piece of knowledge should have a single, authoritative representation.
+- **SOLID**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
+- **Composition over inheritance**: Favour traits and composition over emulating inheritance.
+- **Command-Query Separation**: Functions should either return data or produce side effects, not both.
+- **Principle of Least Astonishment**: Components should behave the way most users expect.
+
+## Performance
+
+- Avoid unnecessary heap allocations. Prefer stack allocation and slices.
+- Use `&[T]` instead of `&Vec<T>` and `&str` instead of `&String` in function signatures.
+- Profile before optimizing. Use `cargo bench` and tools like `criterion`.
+- Stay lazy with iterators. Avoid `.collect()`-ing unnecessarily.
+
+## Dependencies
+
+- Prefer well-maintained, widely-used crates. Pin versions in `Cargo.toml`.
+- Minimize the dependency tree. Avoid adding crates for trivial functionality.
+- Use `cargo audit` to check for known vulnerabilities.
+
+## Documentation
+
+- All public items (`pub`) must have doc comments.
+- Include examples in doc comments for public functions.
+- Use module-level documentation at the top of files.
+
+## Anti-patterns
+
+Common but counterproductive solutions. **Avoid them.**
+
+### Clone to satisfy the borrow checker
+
+Do not resolve borrow checker errors by cloning without understanding the consequences. Cloning creates independent copies — changes to one are not reflected in the other.
+
+If the borrow checker complains, first understand the ownership issue. Use `mem::take`, restructure borrows, or redesign the data flow.
+
+**Exception:** `Rc` and `Arc` are designed for shared ownership via clone. Cloning them is cheap and correct. Deliberate cloning is also fine when ownership semantics require it, or for prototypes and non-performance-critical code.
+
+### `#![deny(warnings)]`
+
+Do not use `#![deny(warnings)]` in crate roots. New compiler versions may introduce new warnings, breaking builds unexpectedly.
+
+Instead, deny specific named lints explicitly, or use `RUSTFLAGS="-D warnings"` in CI. This preserves Rust's stability guarantees while still enforcing lint discipline.
+
+### Deref polymorphism
+
+Do not misuse the `Deref` trait to emulate struct inheritance. `Deref` is designed for smart pointers (`pointer-to-T` → `T`), not for converting between arbitrary types.
+
+It does not introduce subtyping. Traits on the inner type are not automatically available on the outer type. It interacts badly with generics and bounds checking.
+
+Instead, use composition with explicit delegation methods, or use traits for shared behaviour.
+
+## Further reference
+
+Before implementing any non-trivial pattern — builders, visitors, newtype wrappers, RAII guards, command objects, struct decomposition for borrow splitting, etc. — read [idioms.md](references/idioms.md). It covers the community-agreed idioms and design patterns for Rust, and getting these right the first time avoids painful refactors later.

skills/writing-rust/references/idioms.md 🔗

@@ -0,0 +1,147 @@
+# Rust Idioms and Design Patterns
+
+## Contents
+
+- [Idioms](#idioms)
+  - [Borrowed types for arguments](#borrowed-types-for-arguments)
+  - [String concatenation with format!](#string-concatenation-with-format)
+  - [Constructors and Default](#constructors-and-default)
+  - [Collections as smart pointers](#collections-as-smart-pointers)
+  - [Finalisation in destructors](#finalisation-in-destructors)
+  - [mem::take and mem::replace](#memtake-and-memreplace)
+  - [On-stack dynamic dispatch](#on-stack-dynamic-dispatch)
+  - [Iterating over Option](#iterating-over-option)
+  - [Pass variables to closure](#pass-variables-to-closure)
+  - [non_exhaustive for extensibility](#non_exhaustive-for-extensibility)
+  - [Temporary mutability](#temporary-mutability)
+  - [Return consumed argument on error](#return-consumed-argument-on-error)
+- [Behavioural patterns](#behavioural-patterns)
+  - [Command](#command)
+  - [Interpreter](#interpreter)
+  - [Newtype](#newtype)
+  - [RAII with guards](#raii-with-guards)
+  - [Strategy](#strategy)
+  - [Visitor](#visitor)
+- [Creational patterns](#creational-patterns)
+  - [Builder](#builder)
+  - [Fold](#fold)
+- [Structural patterns](#structural-patterns)
+  - [Struct decomposition for independent borrowing](#struct-decomposition-for-independent-borrowing)
+  - [Prefer small crates](#prefer-small-crates)
+  - [Contain unsafety in small modules](#contain-unsafety-in-small-modules)
+  - [Custom traits for complex type bounds](#custom-traits-for-complex-type-bounds)
+- [Functional patterns](#functional-patterns)
+
+## Idioms
+
+### Borrowed types for arguments
+
+Prefer `&str` over `&String`, `&[T]` over `&Vec<T>`, and `&T` over `&Box<T>` in function parameters. This avoids unnecessary indirection and accepts more input types through deref coercion.
+
+### String concatenation with format!
+
+Prefer `format!("Hello {name}!")` over manual `push`/`push_str` chains for readability. For performance-critical paths where the string can be pre-allocated, manual push operations may be faster.
+
+### Constructors and Default
+
+Rust has no language-level constructors. Use an associated function called `new` to create objects. If the type has a sensible zero/empty state, implement the `Default` trait. It is common and expected to provide both `Default` and `new`, even if they are functionally identical.
+
+`Default` enables usage with `or_default` functions throughout the standard library and partial initialization: `MyStruct { field: value, ..Default::default() }`.
+
+### Collections as smart pointers
+
+Implement `Deref` for owning collections to provide a borrowed view (e.g., `Vec<T>` → `&[T]`, `String` → `&str`). Implement methods on the borrowed view rather than the owning type where possible.
+
+### Finalisation in destructors
+
+Use `Drop` implementations to ensure cleanup code runs on all exit paths (early returns, `?`, panics). Assign the guard object to a named variable (not just `_`) to prevent immediate destruction. Destructors are not guaranteed to run in all cases (infinite loops, double panics).
+
+### mem::take and mem::replace
+
+When transforming an enum variant in place, use `mem::take(name)` to move values out without cloning. This avoids the "clone to satisfy the borrow checker" anti-pattern. For `Option` fields, prefer `Option::take()`.
+
+### On-stack dynamic dispatch
+
+When dynamic dispatch is needed but heap allocation is not, use `&mut dyn Trait` with temporary values. Since Rust 1.79.0, the compiler automatically extends lifetimes of temporaries in `&` or `&mut`.
+
+### Iterating over Option
+
+`Option` implements `IntoIterator`, so it works with `.extend()`, `.chain()`, and `for` loops. Use `std::iter::once` as a more readable alternative to `Some(foo).into_iter()` when the value is always present.
+
+### Pass variables to closure
+
+Use a separate scope block before the closure to prepare variables (clone, borrow, move) rather than creating separate named variables like `num2_cloned`. This groups captured state with the closure's definition.
+
+### non_exhaustive for extensibility
+
+Apply `#[non_exhaustive]` to public structs and enums that may gain fields or variants in the future, to maintain backwards compatibility across crate boundaries. Within a crate, a private field (e.g., `_b: ()`) achieves a similar effect. Use deliberately — incrementing the major version when adding fields or variants is often better.
+
+### Temporary mutability
+
+When data must be prepared mutably but then used immutably, use a nested block or variable rebinding (`let data = data;`) to enforce immutability after preparation.
+
+### Return consumed argument on error
+
+If a fallible function takes ownership of an argument, include that argument in the error type so the caller can recover it and retry. Example: `String::from_utf8` returns the original `Vec<u8>` inside `FromUtf8Error`.
+
+## Behavioural patterns
+
+### Command
+
+Separate actions into objects and pass them as parameters. Three approaches: trait objects (complex commands with state), function pointers (simple stateless commands), and `Fn` trait objects (closures). Use trait objects when commands need multiple functions and state; use function pointers or closures for simple, stateless cases.
+
+### Interpreter
+
+Express recurring problem instances in a domain-specific language and implement an interpreter. `macro_rules!` can serve as a lightweight compile-time interpreter for simple DSLs.
+
+### Newtype
+
+Use a tuple struct with a single field to create a distinct type (e.g., `struct Password(String)`). Provides type safety, encapsulation, and the ability to implement custom traits on existing types. Zero-cost abstraction. Consider the `derive_more` crate to reduce boilerplate pass-through impls.
+
+### RAII with guards
+
+Tie resource acquisition to object creation and release to destruction (`Drop`). Use guard objects to mediate access — the borrow checker ensures references cannot outlive the guard. Classic example: `MutexGuard`.
+
+### Strategy
+
+Define an abstract algorithm skeleton and let implementations be swapped via traits or closures. Serde is an excellent real-world example: `Serialize`/`Deserialize` traits allow swapping formats transparently.
+
+### Visitor
+
+Encapsulate an algorithm operating over a heterogeneous collection without modifying the data types. Define `visit_*` methods on a `Visitor` trait for each type; provide `walk_*` helpers to factor out traversal. The visitor can be stateful.
+
+## Creational patterns
+
+### Builder
+
+Construct complex objects step by step using a separate builder type. Provide a `builder()` method on the target type. Return the builder by value from each setter for method chaining: `FooBuilder::new().name("x").build()`. Consider the `derive_builder` crate.
+
+### Fold
+
+Transform a data structure by running an algorithm over each node, producing a new structure. Provide default `fold_*` methods that recurse into children, allowing implementors to override only the nodes they care about. Related to visitor, but produces a new structure.
+
+## Structural patterns
+
+### Struct decomposition for independent borrowing
+
+When the borrow checker prevents simultaneous borrows of different fields in a large struct, decompose into smaller structs. Compose them back; each can then be borrowed independently. Often leads to better design.
+
+### Prefer small crates
+
+Build small, focused crates that do one thing well. Easier to understand, encourage modularity, and allow parallel compilation. Be mindful of dependency quality.
+
+### Contain unsafety in small modules
+
+Isolate `unsafe` code in the smallest possible module that upholds the needed invariants. Build a safe interface on top. This restricts the audit surface.
+
+### Custom traits for complex type bounds
+
+When trait bounds become unwieldy (especially with `Fn` traits), introduce a new trait with a generic `impl` for all types satisfying the original bound. Reduces verbosity and increases expressiveness.
+
+## Functional patterns
+
+Rust supports many functional paradigms alongside its imperative core:
+
+- Prefer declarative iterator chains (`.fold()`, `.map()`, `.filter()`) over imperative loops when they improve clarity.
+- Use generics as type classes. Rust's generic type parameters create type class constraints; different filled-in parameters create different types with potentially different `impl` blocks.
+- Apply YAGNI — many traditional OO patterns are unnecessary in Rust due to traits, enums, and the type system.