O Serde é o framework de serialização e desserialização mais utilizado em Rust, e por boas razões: ele combina ergonomia excepcional com performance de referência mundial. O nome vem de serialize/deserialize, e sua arquitetura baseada em traits permite suportar dezenas de formatos de dados — JSON, TOML, YAML, MessagePack, CBOR, Bincode e muitos outros — tudo com a mesma interface.
O que torna o Serde especial é seu uso extensivo de macros procedurais derive que geram código de serialização em tempo de compilação, resultando em zero overhead em runtime. Não há reflection, não há alocações extras — apenas código otimizado gerado pelo compilador.
Praticamente todo projeto Rust não-trivial utiliza o Serde. Ele é essencial para APIs web, arquivos de configuração, comunicação entre serviços, armazenamento de dados e qualquer cenário que envolva converter estruturas Rust para/de formatos externos.
Instalação
Adicione o Serde e os formatos desejados ao Cargo.toml:
[dependencies]
# Serde core com derive macros
serde = { version = "1.0", features = ["derive"] }
# Formatos de dados (adicione os que precisar)
serde_json = "1.0" # JSON
toml = "0.8" # TOML
serde_yaml = "0.9" # YAML
serde_cbor = "0.11" # CBOR (binário compacto)
bincode = "1.3" # Bincode (binário eficiente)
rmp-serde = "1.1" # MessagePack
csv = "1.3" # CSV
Uso Básico
Derive Macros: Serialize e Deserialize
use serde::{Deserialize, Serialize};
// Derive automático para serializar e desserializar
#[derive(Debug, Serialize, Deserialize)]
struct Usuario {
nome: String,
email: String,
idade: u32,
ativo: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Criar um usuário
let usuario = Usuario {
nome: "Maria Silva".to_string(),
email: "maria@exemplo.com".to_string(),
idade: 28,
ativo: true,
};
// Serializar para JSON
let json = serde_json::to_string_pretty(&usuario)?;
println!("JSON:\n{}", json);
// {
// "nome": "Maria Silva",
// "email": "maria@exemplo.com",
// "idade": 28,
// "ativo": true
// }
// Desserializar de JSON
let json_str = r#"{"nome":"João","email":"joao@exemplo.com","idade":35,"ativo":false}"#;
let usuario2: Usuario = serde_json::from_str(json_str)?;
println!("Desserializado: {:?}", usuario2);
Ok(())
}
Trabalhando com Diferentes Formatos
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Config {
app_nome: String,
porta: u16,
debug: bool,
tags: Vec<String>,
}
fn demonstrar_formatos() -> Result<(), Box<dyn std::error::Error>> {
let config = Config {
app_nome: "MeuApp".to_string(),
porta: 8080,
debug: true,
tags: vec!["web".to_string(), "api".to_string()],
};
// JSON
let json = serde_json::to_string_pretty(&config)?;
println!("=== JSON ===\n{}\n", json);
// TOML
let toml_str = toml::to_string_pretty(&config)?;
println!("=== TOML ===\n{}\n", toml_str);
// YAML
let yaml = serde_yaml::to_string(&config)?;
println!("=== YAML ===\n{}\n", yaml);
// Bincode (binário compacto)
let bytes = bincode::serialize(&config)?;
println!("=== Bincode ===\n{} bytes\n", bytes.len());
// Desserializar de qualquer formato
let de_json: Config = serde_json::from_str(&json)?;
let de_toml: Config = toml::from_str(&toml_str)?;
let de_yaml: Config = serde_yaml::from_str(&yaml)?;
let de_bincode: Config = bincode::deserialize(&bytes)?;
println!("Todos desserializados com sucesso!");
Ok(())
}
Enums com Serde
use serde::{Deserialize, Serialize};
// Enums são serializadas de forma flexível
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "tipo")] // Tagged representation
enum Evento {
#[serde(rename = "usuario_criado")]
UsuarioCriado { nome: String, email: String },
#[serde(rename = "pedido_realizado")]
PedidoRealizado { pedido_id: u64, valor: f64 },
#[serde(rename = "pagamento_confirmado")]
PagamentoConfirmado { pedido_id: u64, metodo: String },
}
fn demonstrar_enums() -> Result<(), Box<dyn std::error::Error>> {
let eventos = vec![
Evento::UsuarioCriado {
nome: "Ana".to_string(),
email: "ana@exemplo.com".to_string(),
},
Evento::PedidoRealizado {
pedido_id: 42,
valor: 199.90,
},
Evento::PagamentoConfirmado {
pedido_id: 42,
metodo: "pix".to_string(),
},
];
for evento in &eventos {
let json = serde_json::to_string_pretty(evento)?;
println!("{}\n", json);
}
// {"tipo":"usuario_criado","nome":"Ana","email":"ana@exemplo.com"}
// {"tipo":"pedido_realizado","pedido_id":42,"valor":199.9}
// {"tipo":"pagamento_confirmado","pedido_id":42,"metodo":"pix"}
// Desserializar automaticamente escolhe a variante correta
let json = r#"{"tipo":"pedido_realizado","pedido_id":99,"valor":49.90}"#;
let evento: Evento = serde_json::from_str(json)?;
println!("Desserializado: {:?}", evento);
Ok(())
}
Representações de Enum
use serde::{Deserialize, Serialize};
// Externally tagged (padrão)
// {"UsuarioCriado": {"nome": "Ana"}}
#[derive(Serialize, Deserialize)]
enum EventoExterno {
UsuarioCriado { nome: String },
PedidoRealizado { id: u64 },
}
// Internally tagged
// {"tipo": "usuario_criado", "nome": "Ana"}
#[derive(Serialize, Deserialize)]
#[serde(tag = "tipo")]
enum EventoInterno {
#[serde(rename = "usuario_criado")]
UsuarioCriado { nome: String },
#[serde(rename = "pedido_realizado")]
PedidoRealizado { id: u64 },
}
// Adjacently tagged
// {"tipo": "usuario_criado", "dados": {"nome": "Ana"}}
#[derive(Serialize, Deserialize)]
#[serde(tag = "tipo", content = "dados")]
enum EventoAdjacente {
#[serde(rename = "usuario_criado")]
UsuarioCriado { nome: String },
#[serde(rename = "pedido_realizado")]
PedidoRealizado { id: u64 },
}
// Untagged
// {"nome": "Ana"} ou {"id": 42}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum EventoSemTag {
UsuarioCriado { nome: String },
PedidoRealizado { id: u64 },
}
Recursos Avançados
Atributos #[serde(…)]
Atributos de Campo
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Debug, Serialize, Deserialize)]
struct Produto {
// Renomear campo no JSON
#[serde(rename = "product_name")]
nome: String,
// Campo opcional (usa None se ausente)
#[serde(skip_serializing_if = "Option::is_none")]
descricao: Option<String>,
// Valor padrão se ausente
#[serde(default)]
quantidade: u32,
// Valor padrão customizado
#[serde(default = "preco_padrao")]
preco: f64,
// Ignorar campo na serialização
#[serde(skip)]
cache_interno: String,
// Apenas serializar (não desserializar)
#[serde(skip_deserializing)]
id_gerado: String,
// Alias para desserialização (aceita múltiplos nomes)
#[serde(alias = "created_at", alias = "criado_em")]
data_criacao: String,
// Serializar/desserializar com função customizada
#[serde(serialize_with = "serialize_tags", deserialize_with = "deserialize_tags")]
tags: Vec<String>,
// Flatten: incorpora campos de outro struct
#[serde(flatten)]
metadata: Metadata,
}
fn preco_padrao() -> f64 {
0.0
}
#[derive(Debug, Serialize, Deserialize)]
struct Metadata {
versao: String,
autor: String,
}
// Funções customizadas de serialização
fn serialize_tags<S>(tags: &[String], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
// Serializar como string separada por vírgulas
serializer.serialize_str(&tags.join(","))
}
fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(s.split(',').map(|t| t.trim().to_string()).collect())
}
Atributos de Container
use serde::{Deserialize, Serialize};
// Renomear todos os campos automaticamente
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ConfigApi {
nome_do_app: String, // -> "nomeDoApp"
porta_http: u16, // -> "portaHttp"
max_conexoes: usize, // -> "maxConexoes"
}
// Outras opções de rename_all:
// "lowercase", "UPPERCASE", "camelCase", "PascalCase",
// "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case",
// "SCREAMING-KEBAB-CASE"
// Negar campos desconhecidos (erro se JSON tiver campos extras)
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct ConfigEstrita {
nome: String,
porta: u16,
}
// Verificação customizada após desserialização
#[derive(Serialize, Deserialize)]
#[serde(try_from = "ConfigRaw")]
struct ConfigValidada {
nome: String,
porta: u16,
}
#[derive(Deserialize)]
struct ConfigRaw {
nome: String,
porta: u16,
}
impl TryFrom<ConfigRaw> for ConfigValidada {
type Error = String;
fn try_from(raw: ConfigRaw) -> Result<Self, String> {
if raw.porta == 0 {
return Err("Porta não pode ser zero".to_string());
}
if raw.nome.is_empty() {
return Err("Nome não pode ser vazio".to_string());
}
Ok(ConfigValidada {
nome: raw.nome,
porta: raw.porta,
})
}
}
Desserialização com serde_json::Value
use serde_json::{json, Value};
fn trabalhar_com_json_dinamico() -> Result<(), Box<dyn std::error::Error>> {
// Construir JSON programaticamente
let dados = json!({
"nome": "Produto X",
"preco": 29.90,
"tags": ["eletrônico", "novo"],
"dimensoes": {
"largura": 10.0,
"altura": 20.0,
"profundidade": 5.0
}
});
// Acessar campos dinamicamente
println!("Nome: {}", dados["nome"]);
println!("Primeira tag: {}", dados["tags"][0]);
println!("Largura: {}", dados["dimensoes"]["largura"]);
// Verificar tipo
if dados["preco"].is_f64() {
let preco = dados["preco"].as_f64().unwrap();
println!("Preço: R$ {:.2}", preco);
}
// Converter Value para tipo concreto
#[derive(serde::Deserialize, Debug)]
struct Dimensoes {
largura: f64,
altura: f64,
profundidade: f64,
}
let dim: Dimensoes = serde_json::from_value(dados["dimensoes"].clone())?;
println!("Dimensões: {:?}", dim);
// Misturar tipado e dinâmico
#[derive(serde::Deserialize, Debug)]
struct ProdutoParcial {
nome: String,
preco: f64,
#[serde(flatten)]
extras: serde_json::Map<String, Value>,
}
let produto: ProdutoParcial = serde_json::from_value(dados)?;
println!("Extras: {:?}", produto.extras);
Ok(())
}
Zero-Copy Deserialization
use serde::Deserialize;
// Zero-copy: empresta dados do buffer original em vez de alocar
#[derive(Debug, Deserialize)]
struct LogEntry<'a> {
#[serde(borrow)]
timestamp: &'a str, // &str em vez de String - sem alocação
#[serde(borrow)]
nivel: &'a str,
#[serde(borrow)]
mensagem: &'a str,
codigo: u32, // tipos Copy são sempre eficientes
}
fn demonstrar_zero_copy() -> Result<(), Box<dyn std::error::Error>> {
let json_data = r#"{
"timestamp": "2024-03-15T10:30:00Z",
"nivel": "INFO",
"mensagem": "Servidor iniciado na porta 8080",
"codigo": 200
}"#;
// Desserializar emprestando do json_data original
let entry: LogEntry = serde_json::from_str(json_data)?;
println!("{}: [{}] {} ({})", entry.timestamp, entry.nivel, entry.mensagem, entry.codigo);
// Processar milhares de entradas sem alocar strings
let logs = vec![json_data; 1000];
let mut total_codigos: u32 = 0;
for log in &logs {
let entry: LogEntry = serde_json::from_str(log)?;
total_codigos += entry.codigo;
}
println!("Soma dos códigos: {}", total_codigos);
Ok(())
}
Serialização Customizada Completa
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
// Tipo que precisa de serialização customizada
#[derive(Debug, Clone)]
struct Cor {
r: u8,
g: u8,
b: u8,
}
// Serializar como string "#RRGGBB"
impl Serialize for Cor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let hex = format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b);
serializer.serialize_str(&hex)
}
}
// Desserializar de string "#RRGGBB"
impl<'de> Deserialize<'de> for Cor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let s = s.trim_start_matches('#');
if s.len() != 6 {
return Err(serde::de::Error::custom("cor deve ter formato #RRGGBB"));
}
let r = u8::from_str_radix(&s[0..2], 16)
.map_err(|_| serde::de::Error::custom("valor R inválido"))?;
let g = u8::from_str_radix(&s[2..4], 16)
.map_err(|_| serde::de::Error::custom("valor G inválido"))?;
let b = u8::from_str_radix(&s[4..6], 16)
.map_err(|_| serde::de::Error::custom("valor B inválido"))?;
Ok(Cor { r, g, b })
}
}
#[derive(Debug, Serialize, Deserialize)]
struct Tema {
nome: String,
cor_primaria: Cor,
cor_secundaria: Cor,
cor_fundo: Cor,
}
fn demonstrar_cor_customizada() -> Result<(), Box<dyn std::error::Error>> {
let tema = Tema {
nome: "Escuro".to_string(),
cor_primaria: Cor { r: 66, g: 133, b: 244 },
cor_secundaria: Cor { r: 52, g: 168, b: 83 },
cor_fundo: Cor { r: 30, g: 30, b: 30 },
};
let json = serde_json::to_string_pretty(&tema)?;
println!("{}", json);
// {
// "nome": "Escuro",
// "cor_primaria": "#4285F4",
// "cor_secundaria": "#34A853",
// "cor_fundo": "#1E1E1E"
// }
let de_volta: Tema = serde_json::from_str(&json)?;
println!("Cor primária: {:?}", de_volta.cor_primaria);
Ok(())
}
Boas Práticas
1. Sempre Use derive Quando Possível
// Bom: derive automático
#[derive(Serialize, Deserialize)]
struct Config {
nome: String,
porta: u16,
}
// Evite: implementação manual desnecessária
// Só implemente manualmente quando realmente precisar de lógica customizada
2. Use rename_all para APIs Externas
// API JavaScript/JSON tipicamente usa camelCase
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RespostaApi {
nome_completo: String, // -> "nomeCompleto"
data_nascimento: String, // -> "dataNascimento"
endereco_email: String, // -> "enderecoEmail"
}
3. Skip Fields Desnecessários
#[derive(Serialize, Deserialize)]
struct Usuario {
nome: String,
email: String,
// Não serializar campos temporários ou internos
#[serde(skip)]
_cache: Option<String>,
// Não serializar None
#[serde(skip_serializing_if = "Option::is_none")]
telefone: Option<String>,
// Não serializar coleções vazias
#[serde(skip_serializing_if = "Vec::is_empty", default)]
tags: Vec<String>,
}
4. Valide na Desserialização
#[derive(Deserialize)]
#[serde(try_from = "ConfigRaw")]
struct Config {
porta: u16,
max_conexoes: usize,
}
#[derive(Deserialize)]
struct ConfigRaw {
porta: u16,
max_conexoes: usize,
}
impl TryFrom<ConfigRaw> for Config {
type Error = String;
fn try_from(raw: ConfigRaw) -> Result<Self, String> {
if raw.porta < 1024 {
return Err(format!("Porta {} é reservada (< 1024)", raw.porta));
}
if raw.max_conexoes == 0 {
return Err("max_conexoes deve ser > 0".to_string());
}
Ok(Config {
porta: raw.porta,
max_conexoes: raw.max_conexoes,
})
}
}
5. Prefira Tipos Fortes a serde_json::Value
// Evite: Value genérico perde type safety
fn processar_ruim(dados: serde_json::Value) {
// Runtime panic se o campo não existir ou tiver tipo errado
let nome = dados["nome"].as_str().unwrap();
}
// Bom: tipo concreto com validação em compile-time
#[derive(Deserialize)]
struct Dados {
nome: String,
}
fn processar_bom(dados: Dados) {
// Compilador garante que nome existe e é String
println!("{}", dados.nome);
}
Exemplos Práticos
Exemplo 1: Parsing de Arquivo de Configuração
use serde::Deserialize;
use std::fs;
use std::path::Path;
#[derive(Debug, Deserialize)]
struct AppConfig {
#[serde(default = "default_app")]
app: AppSection,
#[serde(default)]
banco_dados: DatabaseSection,
#[serde(default)]
logging: LoggingSection,
}
#[derive(Debug, Deserialize)]
struct AppSection {
nome: String,
#[serde(default = "default_porta")]
porta: u16,
#[serde(default)]
debug: bool,
#[serde(default)]
hosts_permitidos: Vec<String>,
}
#[derive(Debug, Deserialize, Default)]
struct DatabaseSection {
#[serde(default = "default_db_url")]
url: String,
#[serde(default = "default_pool_size")]
pool_size: u32,
#[serde(default)]
ssl: bool,
}
#[derive(Debug, Deserialize, Default)]
struct LoggingSection {
#[serde(default = "default_log_level")]
nivel: String,
#[serde(default)]
arquivo: Option<String>,
#[serde(default)]
json: bool,
}
fn default_app() -> AppSection {
AppSection {
nome: "MeuApp".to_string(),
porta: 8080,
debug: false,
hosts_permitidos: vec![],
}
}
fn default_porta() -> u16 { 8080 }
fn default_db_url() -> String { "postgres://localhost/mydb".to_string() }
fn default_pool_size() -> u32 { 10 }
fn default_log_level() -> String { "info".to_string() }
fn carregar_config(caminho: &str) -> Result<AppConfig, Box<dyn std::error::Error>> {
let conteudo = fs::read_to_string(caminho)?;
// Detectar formato pelo extensão
let config: AppConfig = if caminho.ends_with(".toml") {
toml::from_str(&conteudo)?
} else if caminho.ends_with(".yaml") || caminho.ends_with(".yml") {
serde_yaml::from_str(&conteudo)?
} else if caminho.ends_with(".json") {
serde_json::from_str(&conteudo)?
} else {
return Err("Formato não suportado. Use .toml, .yaml ou .json".into());
};
Ok(config)
}
Exemplo 2: Manipulação de Resposta de API
use serde::{Deserialize, Serialize};
// Resposta paginada genérica
#[derive(Debug, Deserialize)]
struct RespostaPaginada<T> {
dados: Vec<T>,
paginacao: Paginacao,
}
#[derive(Debug, Deserialize)]
struct Paginacao {
pagina_atual: u32,
total_paginas: u32,
total_itens: u64,
itens_por_pagina: u32,
}
// Modelo de usuário da API
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct UsuarioApi {
id: u64,
nome_completo: String,
email: String,
#[serde(skip_serializing_if = "Option::is_none")]
avatar_url: Option<String>,
#[serde(rename = "isActive")]
ativo: bool,
#[serde(default)]
permissoes: Vec<String>,
#[serde(with = "timestamp_format")]
criado_em: chrono::DateTime<chrono::Utc>,
}
// Módulo para formato de timestamp customizado
mod timestamp_format {
use chrono::{DateTime, Utc, TimeZone};
use serde::{self, Deserialize, Deserializer, Serializer};
const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.3fZ";
pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = date.format(FORMAT).to_string();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
DateTime::parse_from_rfc3339(&s)
.map(|dt| dt.with_timezone(&Utc))
.map_err(serde::de::Error::custom)
}
}
// Exemplo de request body
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct CriarUsuarioRequest {
nome_completo: String,
email: String,
senha: String,
#[serde(skip_serializing_if = "Option::is_none")]
avatar_url: Option<String>,
}
fn demonstrar_api() -> Result<(), Box<dyn std::error::Error>> {
// Simular resposta da API
let json_resposta = r#"{
"dados": [
{
"id": 1,
"nomeCompleto": "Maria Silva",
"email": "maria@exemplo.com",
"isActive": true,
"permissoes": ["admin", "editor"],
"criadoEm": "2024-01-15T10:30:00.000Z"
}
],
"paginacao": {
"paginaAtual": 1,
"totalPaginas": 5,
"totalItens": 47,
"itensPorPagina": 10
}
}"#;
let resposta: RespostaPaginada<UsuarioApi> = serde_json::from_str(json_resposta)?;
println!("Total de usuários: {}", resposta.paginacao.total_itens);
for usuario in &resposta.dados {
println!("- {} ({})", usuario.nome_completo, usuario.email);
}
// Criar request body
let novo_usuario = CriarUsuarioRequest {
nome_completo: "João Santos".to_string(),
email: "joao@exemplo.com".to_string(),
senha: "senha_segura_123".to_string(),
avatar_url: None,
};
let body = serde_json::to_string(&novo_usuario)?;
println!("Request body: {}", body);
Ok(())
}
Exemplo 3: Streaming JSON de Arquivos Grandes
use serde::Deserialize;
use std::fs::File;
use std::io::BufReader;
#[derive(Debug, Deserialize)]
struct LogLine {
timestamp: String,
level: String,
message: String,
#[serde(default)]
fields: serde_json::Map<String, serde_json::Value>,
}
fn processar_logs_grandes(caminho: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = File::open(caminho)?;
let reader = BufReader::new(file);
// Usar Deserializer de streaming para processar linha por linha
let stream = serde_json::Deserializer::from_reader(reader).into_iter::<LogLine>();
let mut total = 0u64;
let mut erros = 0u64;
for resultado in stream {
match resultado {
Ok(log) => {
total += 1;
if log.level == "ERROR" {
erros += 1;
eprintln!("[ERRO] {}: {}", log.timestamp, log.message);
}
}
Err(e) => {
eprintln!("Erro ao parsear linha: {}", e);
}
}
}
println!("Processados {} logs, {} erros encontrados", total, erros);
Ok(())
}
Comparação com Alternativas
| Característica | Serde (Rust) | Jackson (Java) | Newtonsoft (C#) | encoding/json (Go) |
|---|---|---|---|---|
| Performance | Excelente | Boa | Boa | Boa |
| Zero-copy | Sim | Não | Não | Parcial |
| Derive macros | Sim | Annotations | Attributes | Tags |
| Múltiplos formatos | 20+ | 5+ | 3+ | JSON apenas |
| Tipagem | Forte (compile-time) | Forte (runtime) | Forte (runtime) | Fraca |
| Customização | Muito flexível | Flexível | Flexível | Limitada |
| Overhead runtime | Zero | Reflexão | Reflexão | Reflexão |
| Validação | Via try_from | Annotations | Attributes | Manual |
| Streaming | Sim | Sim | Sim | Sim |
| Tamanho do ecossistema | Enorme | Enorme | Grande | Pequeno |
O Serde se destaca por:
- Zero overhead: código gerado em compile-time, sem reflection
- Flexibilidade de formatos: o mesmo derive funciona para JSON, TOML, YAML, binário e dezenas de outros
- Segurança de tipos: erros detectados em tempo de compilação
- Zero-copy: possibilidade de emprestar dados sem alocação
- Ecossistema vasto: virtualmente toda crate Rust integra com Serde
Conclusão
O Serde é a espinha dorsal da serialização em Rust, e dominá-lo é essencial para qualquer desenvolvedor na linguagem. Com suas derive macros poderosas, suporte a dezenas de formatos e performance excepcional, ele torna o trabalho com dados externos tão natural quanto trabalhar com tipos nativos Rust.
Pontos-chave para lembrar:
derive(Serialize, Deserialize)resolve a maioria dos casos- Atributos
#[serde(...)]permitem customização fina rename_alladapta automaticamente para convenções de outras linguagensskip_serializing_ifgera JSON mais limpoValuepermite trabalhar com dados dinâmicos quando necessário- Zero-copy com
&stre#[serde(borrow)]para máxima performance
Para aprofundar, consulte a documentação oficial do Serde e o guia de atributos.
No próximo passo, explore o Tokio para programação assíncrona em Rust.