What it is
Serde is Rust's serialisation framework, converting between Rust data structures and formats such as JSON, YAML, TOML, MessagePack and many more via derive macros.
Derive Serialize and Deserialize on your types, then use a format crate to convert. Attributes control field names, defaults, skipping and flattening.
Installation
cargo add serde --features deriveGetting started
The smallest useful thing you can do with it, and what each part means.
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Book {
id: u32,
title: String,
#[serde(default)]
tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
subtitle: Option<String>,
}
let json = serde_json::to_string(&book)?;
let parsed: Book = serde_json::from_str(&json)?;#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
enum Event {
Click { x: i32, y: i32 },
KeyPress { key: String },
Close,
}
// {"type":"click","x":10,"y":20}
// Without a tag, Serde tries each variant in order — slower, and the
// error message on failure is much worse.Advanced usage
Where the library earns its place over a simpler alternative.
// Borrow from the input instead of allocating a String per field.
#[derive(Deserialize)]
struct Row<'a> {
#[serde(borrow)]
name: &'a str,
}
// Custom conversion for a field.
fn ms_to_duration<'de, D>(d: D) -> Result<Duration, D::Error>
where D: serde::Deserializer<'de> {
let ms = u64::deserialize(d)?;
Ok(Duration::from_millis(ms))
}
#[derive(Deserialize)]
struct Config {
#[serde(deserialize_with = "ms_to_duration")]
timeout: Duration,
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- missing field `x` at line 1 column 20
- The input lacks a required field. Add #[serde(default)] or make it Option<T> if it is genuinely optional.
- the trait Serialize is not implemented
- A nested type lacks the derive, or the derive feature is not enabled on the serde dependency.
Best practices
- Enable the derive feature rather than implementing the traits by hand.
- Use #[serde(deny_unknown_fields)] on configuration types so typos are errors, not silence.
- Prefer tagged enums; untagged ones are slower and produce unusable error messages.
- Use skip_serializing_if to keep None out of the output rather than emitting null.
Background
Why it exists, and what it was reacting to.
Written by David Tolnay and Erick Tryzelaar, Serde is arguably the most important library in the Rust ecosystem. Its trait-based design means a format crate and a data type written by strangers interoperate with no coordination.
