Zig vs Rust em 2026
(zackoverflow.dev)- Há cerca de 3 anos, quando o autor escreveu diretamente uma VM de bytecode e um garbage collector em Zig e
unsafe Rust, a ergonomia amigável para humanos do Zig levava vantagem, mas com a chegada da era dos agentes de código essa vantagem se tornou praticamente irrelevante - O ganho de produtividade de desenvolvedor de 1,5x a 5x oferecido pelos principais recursos do Zig é esmagado pelo ganho de produtividade de 100x proporcionado por agentes de código baseados em Rust
- A interface de allocator do Zig, inteiros com largura de bits arbitrária,
packed struct, comptime e outros recursos centrais brilham quando humanos escrevem código manualmente - O sistema de tipos do Rust é mais eficaz para evitar, em tempo de compilação, erros de agentes por meio de bounded polymorphism e imposição de invariantes
- Em um cenário em que a quantidade de código gerado por agentes aumentou 100x, as garantias de segurança de memória do Rust se tornam uma vantagem decisiva em relação ao Zig
Mudança principal
- O Zig tinha uma grande vantagem na ergonomia de código unsafe, mas à medida que a proporção de código escrito diretamente por humanos diminui, o valor prático dessa vantagem também cai
- O ganho de produtividade para desenvolvedores humanos na faixa de 1,5x a 5x trazido pelos recursos do Zig fica ofuscado pelo ganho de 100x ao usar agentes de código com Rust
- Boa parte dos recursos mais representativos do Zig aumenta a conveniência de escrever código manualmente, mas essa diferença não importa tanto para agentes de código
- Há cerca de 3 anos, ao escrever uma VM de bytecode e um garbage collector em Zig e unsafe Rust, o autor sentiu que a experiência de escrever código unsafe era melhor no Zig
- Mesmo em 2026, Zig continua sendo uma boa linguagem, mas Rust passou a ser a linguagem mais preferida e a que combina melhor com agentes de código
A interface de allocator do Zig
- A interface de allocator do Zig facilita aplicar allocators especializados, como arena e stack fallback, para otimizar caminhos específicos de código
- Ao ler uma linha de entrada do usuário, o comprimento da entrada é teoricamente ilimitado, então seria necessário um heap allocator, mas na prática a maioria das entradas são termos de busca curtos ou caminhos, bem menores que 1 KB
std.heap.stackFallback(256, heap_allocator)coloca um buffer de tamanho fixo na stack e só passa para o heap quando a entrada estoura esse limite, permitindo tratar o caso comum sem alocação no heap- No passado, Rust não tinha um recurso equivalente à interface
Allocatordo Zig, então se fosse necessário umVec<T>com allocator customizado, era preciso copiar a implementação da biblioteca padrão e modificá-la - O código-fonte das coleções do Bumpalo era uma forma de fazer fork das coleções padrão e conectá-las a um bump allocator
- Rust nightly teve por um tempo o trait
Allocator, e agora ele parece ter chegado a um nível suficientemente bom - O
Allocatordo Rust é baseado em trait e, por isso, usa despacho estático, enquanto os allocators do Zig são baseados em vtable - Rust não tem uma convenção difundida na comunidade de projetar estruturas de dados com base em parâmetros de allocator como no Zig, mas essa limitação se torna menos importante à medida que a IA fica mais capaz de copiar e adaptar código
Inteiros com largura de bits arbitrária e packed struct
- Os inteiros com largura de bits arbitrária do Zig e
packed structfacilitam tarefas como otimização de cache de CPU em design orientado a dados, tagged pointer, NaN boxing e bitflags - Ao usar APIs de Obj-C com Metal por meio da Objective-C runtime C API,
idpode ser um tagged pointer, e não um ponteiro para objeto no heap alinhado - Se um
NSNumbertagged for passado para código que assume alinhamento, pode ocorrer UB, então é preciso checar de forma barata se é “um ponteiro de heap ou um immediate tagged” - Em um layout simplificado de tagged pointer do Objective-C, o bit menos significativo indica “não é ponteiro de heap”, os 3 bits seguintes identificam o slot da classe, e os 60 bits restantes formam o payload
- No Zig, com
enum(u3)epacked struct, é possível expressar diretamente o layout de bits no tipo, comoclass: TaggedClass,payload: u60 - No Zig, é possível alternar entre um
u64bruto eObjcTaggedPointercom@bitCast, e emis_ns_numberchecaris_taggede a classe.ns_number - O código correspondente em Rust mantém constantes como
TAG_MASK,CLASS_MASK,CLASS_SHIFTePAYLOAD_SHIFTdentro deObjcTaggedPointer(u64), faz operações OR na criação e aplica máscaras no acesso - No Rust, o slot de classe não é um tipo real, mas uma constante
u64, e escrever isso manualmente é menos ergonômico do que no Zig - Em Rust, é melhor usar crates como bitfield ou bitflags, mas ambos dependem de proc macros e ainda não parecem tão bons quanto
packed structdo Zig - Com agentes de código, o problema de ser chato escrever esse tipo de código à mão diminui bastante
A mudança no valor de comptime
- O comptime do Zig é seu recurso mais chamativo, e, tirando algumas linguagens esotéricas com tipos dependentes, quase nenhuma linguagem oferece avaliação em tempo de compilação tão boa quanto o Zig
- No uso real, o autor passou a sentir menos falta de comptime, e cerca de 95% do uso era para criar estruturas de dados genéricas com tipos parametrizados
- Um padrão representativo é
fn ArrayList(comptime T: type) type, que recebe um tipo e retorna um tipo de struct comitems: []T,capacity: usize,allocator: Allocator - O sistema de tipos do Rust substitui boa parte dos genéricos no estilo comptime do Zig e consegue impor mais condições invariantes
- Nos cerca de 5% restantes dos casos, a ausência de comptime é incômoda, e a única alternativa confiável é codegen
- Ao desenvolver um jogo, quando se deseja hardcodar em uma estrutura de dados os dados de geometria de hitbox gerados por ferramentas, em Rust é preciso pedir ao Claude para escrever um script que gere o arquivo Rust
- Ainda assim, avaliação em tempo de compilação não é algo realmente necessário com tanta frequência
As vantagens do sistema de tipos do Rust
- O sistema de tipos do Rust é visto como uma troca mais valiosa do que o comptime do Zig, especialmente na área de traits/typeclasses para bounded polymorphism
- Tentar implementar no Zig o mesmo nível de bounded polymorphism é muito difícil
- O sistema de tipos do Rust consegue impor mais invariantes, o que ajuda a bloquear erros comuns cometidos por agentes de código
- Em código de jogos, o crate euclid é usado para evitar um problema comum em programação gráfica: confundir espaços de coordenadas
- Ao criar tipos especializados para cada espaço de coordenadas, como
Point<Screen>ouPoint<World>, misturar por engano coordenadas de mundo e de tela passa a ser algo bloqueado em tempo de compilação - Com tipos separados como
WorldPoint,WorldVectoreScreenPoint, a soma de point e vector dentro do mesmo espaço continua permitida - Com
Translation2D::<f32, WorldSpace, ScreenSpace>, é possível converter explicitamente do espaço do mundo para o espaço da tela - Em contrapartida, código como
let bad: ScreenPoint = player;, que atribui diretamente umWorldPointaScreenPoint, não é permitido
O efeito de lidar menos com problemas de memória
- Se agentes de código tornam possível escrever 100x mais código, a quantidade de código Zig que precisa ser revisada em busca de problemas de memória também aumenta 100x
- Sem verificação formal, a área da superfície do espaço de busca que precisa ser examinada para encontrar bugs se torna muito maior
- Na situação atual, em que a quantidade de código gerado é grande, Rust se torna mais atraente
- O trade-off tradicional do Rust era prejudicar a produtividade do desenvolvedor quando ele ainda não estava acostumado com o borrow checker, mas com agentes de código essa desvantagem perde muita importância
- Mesmo usando
unsafeem Rust, é possível fazer o agente de código rodar ferramentas como miri para verificar se não ocorre UB e se as regras de aliasing do Rust não estão sendo violadas
Conclusão
- Zig continua sendo uma linguagem de que se sente falta e continua sendo uma boa linguagem
- Na forma de trabalhar de 2026, Rust é mais preferido, e sua compatibilidade com agentes de código também é melhor
1 comentários
Opiniões no Lobste.rs
Um antigo tech lead meu tinha uma opinião bem forte de que código copiado e colado nem sempre é algo ruim
Por causa do princípio DRY, isso soava instintivamente errado ou polêmico, mas ele era uma pessoa muito pragmática e aplicava esse princípio principalmente a grandes bases de código de teste
A lógica era que, em vez de forçar interfaces compartilhadas “espertas”, uma base de código simples, porém maior e mais redundante, pode ser mais fácil de manter
Tenho voltado a esse mesmo pensamento ao usar LLMs hoje em dia, e agora aplico isso até em partes mais importantes do software
Gerar código é rápido, e parece provável que LLMs também acertem melhor em bases de código simples, mas redundantes
Quando você coloca abstração demais nos testes para reduzir duplicação, os testes ficam mais difíceis de entender e ainda correm o risco de estarem sutilmente errados
Pior ainda: se você reutiliza a abstração do código que está sendo testado, o teste também pode errar da mesma forma que esse código
Além disso, ao contrário do código da aplicação, testes são praticamente “componíveis” de graça
A menos que você tenha estragado seriamente o test harness, adicionar ou remover testes à vontade não afeta os outros testes, e como não há atrito de integração, existe um motivo a menos para evitar duplicação
Já vi isso ser descrito em testes como DAMP: “Descriptive and Meaningful Phrases”, um princípio que enfatiza legibilidade acima de exclusividade
Esse princípio pode gerar duplicação no sentido de repetir código parecido, mas faz com que os testes pareçam mais obviamente corretos
https://testing.googleblog.com/2019/12/…
A comunidade Go tem algo parecido: “um pequeno copy é melhor do que uma pequena dependência” https://go-proverbs.github.io/
Sou grato por ele compartilhar sessões de live coding
Se me lembro direito, quando começava algo, ele muitas vezes encontrava o código mais parecido com o que queria fazer, copiava tudo e então modificava a partir dali
Eu pensava algo como “você não vai sentar e refletir por um tempão sobre qual abstração os dois compartilham?”, mas ele simplesmente seguia no copia-e-cola e era muito mais produtivo do que eu
Interessante pensar no timing da reescrita com IA de Zig → Rust do Bun https://xcancel.com/jarredsumner/status/2053063524826620129#m
Desses, 75 não teriam compilado em uma linguagem com destrutores, semântica de movimento e borrow checker
Ou seja, um em cada três PRs enviados era basicamente “esquecemos de liberar no caminho de erro”
Cerca de 88 dos 108 estavam no Zig, e os ~14 do lado C++ seriam em sua maioria categorias residuais que sobrariam em qualquer linguagem, como ciclos de referência e races de concorrência no GC
Então a diferença entre Zig→Rust é real, e os bugs em Zig são exatamente do tipo que destrutores e ownership corrigem, enquanto o lado C++ já está perto do piso
Sem garantias mais fortes em tempo de compilação, isso continua sendo um jogo de gato e rato
A proposta é parar de corrigir individualmente a maior categoria de bugs e removê-la estruturalmente
– bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
A parte de “nos 5% restantes, sem comptime é sofrido, e a única forma de chegar de maneira confiável a um resultado equivalente é geração de código” não deixa claro o que o autor quer dizer
Isso porque ele não fala nada sobre macros procedurais
Dá um pouco de trabalho fazer direito, mas dá para fazer muita coisa
Também acho que geração de código leva má fama meio à toa
Já resolvi muitos problemas irritantes com geração de código usando scripts
build.rs, e funciona bemClaro, talvez eu me arrependa depois
O argumento central do texto parece ser mais ou menos este
Rust é de fato uma boa linguagem, mas ainda assim isso é um pouco exagerado
Parece propaganda de agente de programação
Isso vem de um texto linkado do mesmo autor: um erro muito comum em programação gráfica é confundir espaços de coordenadas, e o sistema de tipos é poderoso o bastante para expressar em tipos quais espaços de coordenadas e transformações são válidos
Dá para dizer o mesmo sobre moedas, distâncias e pesos em SI e no sistema imperial, strings validadas versus strings fornecidas pelo usuário e segredos etc.
E, se bem administrados, tipos de estado também podem impedir estados impossíveis ou não sound
Mas, pessoalmente, o recurso que eu mais quero no Rust é a eliminação completa de data races
Linguagens gerenciadas também têm data races
Esse papo de “é só usar Go” ignora que, em Go, tudo é mutável por referência entre threads, ainda com malabarismo de slices por cima
Até JavaScript, que ficava entre as opções mais puras e seguras e que antigamente era uma linguagem totalmente de brinquedo, tem toda chamada
awaitcomo uma corrida potencialIsso sem nem entrar na maldade do padrão everything-is-an-EventEmitter
Então sim. Se ao menos tivesse GC… 🤫
Parece que está escondendo um pouco o principal
Agentes de programação também lidam muito bem com Python e JavaScript
Se isso é melhor do que Rust já entra no campo do achismo subjetivo, mas mesmo assim eu não escolheria essas linguagens para muitas tarefas
Fico na dúvida se o problema está no fato de os recursos do Zig mudarem com frequência, ou se é simplesmente por ser uma linguagem mais nova e os dados de treinamento de IA ficarem mais contaminados
Sinto que Zig é mais difícil de escrever do que Rust, mas mais fácil de ler
Na era da IA, lê-se mais código do que se escreve, então acabo preferindo o lado do Zig
Diante do volume de código que está sendo gerado agora, dizer que Rust é mais atraente é só o primeiro passo
Quanto mais os computadores escreverem código, mais vantagem terão as linguagens formais
Isso parece mais uma etapa da discussão sobre tipagem dinâmica
Algo como: “tipagem dinâmica é mais fácil para humanos, então por que eu deveria explicitar a mesma coisa três vezes por causa da máquina?”
Tipos, lifetimes… o que mais existe que seja mais fácil para a máquina escrever e consumir?
Fico curioso para saber quão difícil vai ficar para humanos codificarem diretamente nas linguagens em que os computadores do futuro vão escrever código