rust-serdelisted
Install: claude install-skill rewrite-rs/skills
# Rust Serde
A `#[derive(Deserialize)]` is a claim that anything it accepts is already valid
for the domain — the parse is the only place that claim is cheap to enforce.
## Deserialization is a parse
The type you deserialize into is the type the rest of the program trusts, so it
is the last place validation is cheap. A `Config` that deserializes with a
`String` port and checks it in `run()` has moved the failure past every layer
that could have reported it usefully. What the validated type should be is
`/type-driven-design`; this skill owns the boundary that type crosses.
## `#[serde(try_from = "...")]` is the mechanism
Deserialize into a raw shape, convert with `TryFrom` into the validated type,
and the conversion failure becomes a deserialization error — the read fails,
not the first use of the value. The error must implement `std::error::Error`.
```rust
#[derive(serde::Deserialize)]
struct RawConfig {
workers: u32,
}
#[derive(serde::Deserialize)]
#[serde(try_from = "RawConfig")]
struct WorkerCount(u32);
#[derive(Debug)]
struct ZeroWorkers;
impl std::fmt::Display for ZeroWorkers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "workers must be at least one")
}
}
impl std::error::Error for ZeroWorkers {}
impl TryFrom<RawConfig> for WorkerCount {
type Error = ZeroWorkers;
fn try_from(raw: RawConfig) -> Result<Self, Self::Error> {
if raw.workers == 0 { Err(ZeroWorkers) } else { Ok(WorkerCount(raw.w