Enums & Tagging
Controle como enums Rust mapeiam para JSON com os atributos de tagging do serde - crítico para APIs polimórficas e fluxos de eventos.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
enum Event {
Click { x: i32, y: i32 },
KeyPress { key: String },
}
// {"type":"Click","data":{"x":1,"y":2}}Quando usar isso: APIs que transmitem mensagens discriminadas por variante que JavaScript ou outras linguagens devem analisar de forma confiável.
Exemplo de Trabalho
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
enum Message {
Text { body: String },
Image { url: String, width: u32 },
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
enum LooseId {
Number(u64),
Text(String),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", content = "value")]
enum Payment {
Card { last4: String },
Bank { routing: String },
}
fn main() {
let m = Message::Text { body: "hi".into() };
let json = serde_json::to_string(&m).unwrap();
assert!(json.contains(r#""type":"Text""#));
let id_num: LooseId = serde_json::from_str("42").unwrap();
let id_str: LooseId = serde_json::from_str(r#""abc""#).unwrap();
assert!(matches!(id_num, LooseId::Number(42)));
let p = Payment::Card { last4: "4242".into() };
let pjson = serde_json::to_string(&p).unwrap();
assert!(pjson.contains(r#""kind":"Card""#));
}O que isso demonstra:
- Internamente marcado (
tag = "type"): nome da variante como campo discriminador. - Adjacente marcado (
tag+content): separa objetos de tipo e carga útil. - Sem marcação (
untagged): tenta cada variante em ordem até que uma corresponda.
Mergulho Profundo
Estilos de Tagging
| Estilo | Atributo | Formato JSON |
|---|---|---|
| Externo (padrão) | none | {"Text":{"body":"hi"}} |
| Interno | tag = "type" | {"type":"Text","body":"hi"} |
| Adjacente | tag + content | {"type":"Text","data":{"body":"hi"}} |
Sem marcação (untagged) | untagged | Sem wrapper - o formato implica a variante |
Achatado (flatten) | tag + content + campos achatados | Específico da variante |
Notas Rust
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "event")]
enum Audit { UserCreated { id: u64 }, UserDeleted { id: u64 } }Armadilhas
- A ordem de
untaggedimporta - Formatos ambíguos desserializam para a primeira variante correspondente. Correção: Prefira tags explícitas para APIs públicas. - Derivação de renomeação de variante - Renomear variantes Rust quebra clientes. Correção:
#[serde(rename = "legacy_name")]para compatibilidade. - Variantes de unidade - Marcadas externamente serializam como strings em algumas configurações. Correção: Snapshot do JSON esperado por variante.
- Enums genéricos - Tagging com genéricos requer limites cuidadosos. Correção: Aliases de tipo concreto na fronteira da API.
- Internamente marcado + variantes de tupla - Combinação não suportada. Correção: Use variantes de struct ou tagging adjacente.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
Campo type + struct | Polimorfismo manual | Muitas variantes com regras de tagging compartilhadas |
serde_json::Value | Eventos totalmente dinâmicos | Você precisa de segurança em tempo de compilação |
| Protobuf oneof | Serviços gRPC | APIs REST JSON |
FAQs
Qual tagging para REST?
Tags internas ou adjacentes são mais amigáveis para JavaScript.
pular campo de tag?
#[serde(tag = "t", skip)] avançado - veja a documentação de enum do serde.
achatar enum em struct?
Combine flatten no campo struct que contém o enum.
variantes desconhecidas?
Use #[serde(other)] na variante de fallback (serde 1.0+).
deny_unknown_fields em enum?
Aplique em variantes de struct individualmente.
SCREAMING_SNAKE?
rename_all no contêiner do enum para nomes em estilo constante.
Testes de ida e volta?
Teste o snapshot JSON de cada variante no CI.
OpenAPI oneOf?
Mapeia para enums internamente/adjacente marcados com geradores de código.
Versionar eventos?
Envolva em Envelope { v: u32, event: Event }.
Formatos binários?
Atributos de tagging também afetam o layout do MessagePack - teste por formato.
Relacionados
- Personalizando Serialização - Atributos de campo
- Serialização (Des)personalizada - Implementações manuais de enum
- JSON com serde_json - Exemplos em JSON
- Noções Básicas de Design de API - Contratos de API
- Melhores Práticas do serde - Compatibilidade futura
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.