Add carmack.rust.md agent-specific language prompt

Rust-specific readability guidance for the carmack agent including:
- let...else example for shallow control flow
- Async: don't block the runtime (tokio::fs, spawn_blocking, Send)
- Visibility: prefer pub(crate), private fields with accessors
- Generics: impl Trait over explicit params, avoid complex where clauses
- Improved iterator guidance: if you need a comment, use a loop
- UTF-8 string slicing warnings
- Ownership/lifetime pragmatism
- Anti-patterns: no macros/typestate/proc-macros unless already in repo

Also adds Rust detection to LANGUAGE_PROMPTS (empty base prompt,
agent-specific prompts handle the guidance).
This commit is contained in:
Dhanji R. Prasanna
2026-01-15 07:13:56 +05:30
parent 6d1aa62ba7
commit 65807eea99
2 changed files with 74 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
Prefer **obvious, readable Rust** over cleverness.
## Keep control flow shallow
```rust
let Some(user) = get_user(id) else {
return Err(anyhow!("user not found"));
};
```
- Prefer early returns (`return`, `?`) over deep nesting.
- Use `let ... else { ... }` to reduce indentation.
- Prefer `match` when it clarifies exhaustiveness; prefer `if let` for single-arm cases.
## Name things for humans
- Use concrete, intention-revealing names:
- `bytes`, `chars`, `graphemes` (don't call everything `data`)
- `request`, `response`, `state`, `config`, `opts`
- `*_path`, `*_dir`, `*_id`, `*_idx` (be explicit about units)
- If something is a char index, `*_char_idx`, etc.
## Make invariants explicit in types when cheap
- Prefer newtypes for confusing primitives (`UserId`, `SessionId`) when it reduces mistakes.
- Prefer enums over magic strings/ints for state.
## Prefer small helper functions over mega-blocks
- Extract helpers when a block:
- has >1 responsibility
- has nested matches/loops
- repeats subtle conditions
- Helpers should have crisp names and minimal parameter lists.
## Error handling: clarity > cleverness
- Avoid `unwrap()` / `expect()` in production paths.
- Use `anyhow`/`thiserror` patterns already present in the repo; don't introduce new error stacks casually.
- Add context where it matters (`.context("...")`) but don't spray context everywhere.
## Iterators vs loops
- Prefer a `for` loop when it's clearer than an iterator chain.
- If you need a comment to explain an iterator chain, use a loop instead.
- Use `collect::<Result<Vec<_>, _>>()?` when it's idiomatic *AND* readable; otherwise a loop with `push` is fine.
## Strings are UTF-8: do not do byte slicing
- Never slice `String`/`&str` using byte indices (`s[a..b]`) unless you can prove ASCII.
- Prefer:
- `char_indices()` for char-aware operations
- `unicode-segmentation` graphemes when user-perceived characters matter
- helper functions in-repo (if present) for safe truncation/slicing
## Ownership/lifetimes: avoid "lifetime gymnastics"
- Prefer owned values at module boundaries if lifetimes complicate readability.
- Avoid returning references tied to complex internal state unless there's a strong perf reason.
## Async: don't block the runtime
- Never call blocking I/O (`std::fs`, `std::net`) in async functions without `spawn_blocking`.
- Prefer `tokio::fs` over `std::fs` in async contexts.
- Keep futures `Send` unless you have a specific reason not to.
## Visibility: minimize pub surface
- Prefer `pub(crate)` over `pub` for internal APIs.
- Keep struct fields private with accessor methods when invariants matter.
## Generics: prefer simplicity
- Prefer `fn process(items: impl Iterator<Item = T>)` over `fn process<I: Iterator<Item = T>>(items: I)` when there's only one generic parameter.
- Avoid trait bounds that require reading three lines of `where` clauses.
## What *NOT* to do
- Do not introduce macros or advanced patterns (typestate, proc-macros, async trait hacks) unless the repo already uses them and it clearly improves readability.
- Do not add `dbg!()` calls in committed code.
- Do not create "god files"—split by responsibility when a module grows unwieldy.