Files
marvin-turnstone/skills/rust-best-practices/SKILL.md
T
marvin 18fdff7d8c Import 13 skills from the old opencode setup into turnstone
skills/ carries the SKILL.md files; scripts/import_skills.py runs
inside the VM via turnstone's own storage layer (SKILL.md parser +
risk scanner) so rows match what the console admin API would write.
Server must be restarted after import (stale WAL snapshot).
2026-09-28 20:24:47 +02:00

85 lines
2.3 KiB
Markdown

---
name: rust-best-practices
description: >
Best-practices for Rust software. Load into a workstream whenever you need to read or write a Rust (.rs) file.
---
# Best practices for programming Rust
- Run `cargo check`, `cargo clippy`, and `cargo clippy` before comitting to git.
- Don't ignore warnings from cargo.
- Don't supress warnings using e.g. `#[allow(warning)]`
- Never use `unsafe` without asking the user for explicit permission.
When setting up a new rust project, enable these lints:
```toml
[lints.clippy]
allow_attributes = "warn"
as_ptr_cast_mut = "warn"
as_underscore = "warn"
borrow_as_ptr = "warn"
implicit_clone = "warn"
undocumented_unsafe_blocks = "warn"
unicode_not_nfc = "warn"
unused_async = "deny"
wildcard_dependencies = "deny"
[lints.rust]
absolute_paths_not_starting_with_crate = "deny"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "deny"
missing_abi = "deny"
non_ascii_idents = "forbid"
rust_2018_idioms = { level = "deny", priority = -1 }
single_use_lifetimes = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
```
## Newtypes and "Parse, Don't Validate"
Use newtypes to make invalid states unrepresentable. Instead of passing raw strings and validating at every use site, parse once into a validated type.
### Pattern
```rust
pub struct Email(String); // private field prevents construction without validation
impl Email {
pub fn parse(s: &str) -> Result<Self, ParseError> {
if !s.contains('@') {
return Err(ParseError::InvalidEmail);
}
Ok(Self(s.to_owned()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
```
### Guidelines
- **Private fields**: Force construction through `parse()` only
- **Parse at boundaries**: Validate at system edges (API handlers, CLI args, config loading)
- **Use the type in signatures**: `fn create_user(email: Email)` not `fn create_user(email: &str)`
- **No re-validation**: Once parsed, the type guarantees validity
### When to Apply
- Email addresses, passwords, phone numbers
- File paths, URLs, identifiers
- Any domain value with invariants
- Configuration values requiring validation
### Anti-pattern to Avoid
```rust
// BAD: validation scattered, easy to forget
fn handler(email: &str) {
if !validate_email(email) { return Err(...); }
create_user(email) // still just &str, could be invalid
}
```