← ClaudeAtlas

rust-serdelisted

Serde as the boundary where untrusted input becomes a domain type — rename_all, default, skip_serializing_if, flatten, the four enum representations, deny_unknown_fields, and try_from validation. Use when deriving Serialize or Deserialize, when choosing an enum wire representation, when a JSON or YAML shape does not match the Rust type, or when the user asks how to validate deserialized data.
rewrite-rs/skills · ★ 2 · Data & Documents · score 73
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