HeadlinesBriefing favicon HeadlinesBriefing.com

Repensar a serialização Rust: Desafios de Deser diante das limitações do Serde

Hacker News •
×

Serde é uma incrível biblioteca de serialização para Rust e tem sido uma grande razão pela qual me senti produtivo com ela por anos. No entanto, já trabalhando no Sentry, fiquei bastante frustrado com algumas das limitações com as quais ele se confronta, mas na verdade substituir o Serde é complicado devido ao poder que ele tem no ecossistema. Também porque é bastante difícil realmente fazer melhor sem também fazer alguns compromissos potencialmente dolorosos.

Aqui estão três exemplos de casos de borda do Serde que mostram más interações entre as funcionalidades do Serde ou limitações inesperadas: Um número que é um mapa. Uma enumeração internamente marcada, com a funcionalidade serde_json arbitrary_precision ligada. Achatamento quebra chaves inteiras.

Adaptadores não se compõem. Nenhum desses são bugs que são fáceis de corrigir no Serde. Eles decorrem de seu design, e esse design é protegido pelas garantias de estabilidade do Serde.

Em 2022, eu comecei um experimento chamado Deser. É uma biblioteca de serialização para Rust que coloca a experiência do usuário do Serde em cima de uma arquitetura completamente diferente, inspirada no miniserde. Eu nunca realmente a completei e ela ficou parada por alguns anos. Eu a retomei e ela atingiu agora um ponto em que acho que vale a pena olhar.

Mesmo apenas para inspirar outros a verem se eles quiserem explorar o espaço. O nome e a ideia O nome é Serde com suas duas metades trocadas. Deser é Serde, mas da outra maneira.

Em Serde, um tipo impulsiona o processo de desserialização: uma implementação de Deserialize pede ao desserializador o tipo de valor que ele espera, o formato faz uma chamada de volta para um visitante. Cada valor aninhado é manipulado por recursão, o que faz a desserialização do Serde crescer a pilha com cada nível de aninhamento. Deser, por outro lado, inverte isso e o formato diz o tipo do próximo valor e empurra eventos para um sink.

Quando um sink atinge o início de um valor aninhado, ele não chama nele mas devolve um novo sink a um driver, que mantém todo o estado no heap (de fato, em uma arena). No caminho de saída, emissores retornam seus valores aninhados em vez de recursão para dentro deles.