1 pontos por GN⁺ 2 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • 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 Allocator do Zig, então se fosse necessário um Vec<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 Allocator do 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 struct facilitam 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, id pode ser um tagged pointer, e não um ponteiro para objeto no heap alinhado
  • Se um NSNumber tagged 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) e packed struct, é possível expressar diretamente o layout de bits no tipo, como class: TaggedClass, payload: u60
  • No Zig, é possível alternar entre um u64 bruto e ObjcTaggedPointer com @bitCast, e em is_ns_number checar is_tagged e a classe .ns_number
  • O código correspondente em Rust mantém constantes como TAG_MASK, CLASS_MASK, CLASS_SHIFT e PAYLOAD_SHIFT dentro de ObjcTaggedPointer(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 struct do 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 com items: []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> ou Point<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, WorldVector e ScreenPoint, 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 um WorldPoint a ScreenPoint, 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 unsafe em 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

 
GN⁺ 2 시간 전
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

    • Especialmente em testes, sou totalmente do lado WET, ou seja, “vamos gostar de digitar”
      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
    • Concordo
      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/
    • Quando li Repeat yourself, do more than one thing, and rewrite everything pela primeira vez, isso realmente bateu forte em mim
    • A parte de que isso soava instintivamente errado, na verdade, nunca esteve errada desde o começo
    • Aprendi uma boa lição vendo Andrew Kelley programar
      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

    • Dos 150 PRs recentes mesclados no Bun, 108 eram relacionados à segurança de memória, com problemas como esquecer limpeza em caminhos de erro, use-after-free, leituras não inicializadas, acessos fora dos limites e reentrância
      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

    • O comptime do Zig é realmente incrível, mas o sistema de macros do Rust também está longe de ser algo desprezível
      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 bem
      Claro, talvez eu me arrependa depois
  • O argumento central do texto parece ser mais ou menos este

    1. Num caso específico de customizar alocadores por estrutura de dados, o custo de copiar e colar código era um problema difícil
    2. Alguns recursos do sistema de tipos do Rust são mais convenientes. Ainda assim, é difícil acreditar que, nesse exemplo, não daria para mandar um agente portar o design de tipos do Rust para Zig e impor o formato da API com comptime
    3. Em recursos como bit flags ou SoA, a legibilidade do código não é importante
    4. Se o compilador garante a ausência de erros de segurança de memória, dá para revisar “100 vezes” mais código
      Rust é de fato uma boa linguagem, mas ainda assim isso é um pouco exagerado
  • Parece propaganda de agente de programação

    • Parece uma reflexão sincera sobre como agentes de programação influenciam a escolha da linguagem de programação
    • Ou talvez seja propaganda reversa para Zig…
  • 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 await como uma corrida potencial
    Isso 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