30 pontos por GN⁺ 2025-12-06 | 1 comentários | Compartilhar no WhatsApp
  • Com foco nas diferenças de filosofia e valores entre as três linguagens, o texto compara quais problemas cada uma tenta resolver
  • Go é descrita como uma linguagem que prioriza simplicidade e estabilidade, minimizando recursos para facilitar colaboração e manutenção
  • Rust busca segurança e desempenho ao mesmo tempo, garantindo segurança de memória com um sistema de tipos complexo e uma estrutura de traits
  • Zig é retratada como uma linguagem experimental que dá controle total ao desenvolvedor por meio de gerenciamento manual de memória e design orientado a dados
  • As abordagens contrastantes das três linguagens revelam o sistema de valores implementado por uma linguagem de programação, e a escolha passa a depender de com qual filosofia o desenvolvedor mais se identifica

Perspectiva para comparar linguagens

  • O autor busca entender o sistema de valores de cada linguagem por meio da experimentação com novas linguagens, e não pelas linguagens usadas no trabalho
  • Em vez de apenas comparar listas de recursos, ele enfatiza que o importante é quais trade-offs a linguagem escolheu
  • Go, Rust e Zig se sobrepõem bastante em termos funcionais, mas os valores priorizados pelos seus projetistas são diferentes
  • Ao compreender a filosofia de cada linguagem, é possível avaliar melhor para quais ambientes e objetivos ela é mais adequada

Go — uma linguagem centrada em simplicidade e colaboração

  • Go se distingue pelo minimalismo, com a característica de ser uma linguagem que “cabe inteira na cabeça”
    • Generics só foram adicionados após 12 anos, e recursos como tagged unions ou syntactic sugar para tratamento de erros ainda não existem
  • É extremamente cautelosa ao adicionar recursos; há bastante boilerplate, mas em troca a estabilidade e a legibilidade da linguagem são altas
  • Os slices de Go abrangem o papel de Vec<T> em Rust e ArrayList em Zig, com o runtime gerenciando automaticamente a localização da memória
  • Surgiu da insatisfação com a complexidade do C++ e a lentidão na compilação, tendo sido projetada com foco em simplicidade e compilação rápida
  • Prioriza a eficiência de colaboração em ambientes corporativos, colocando clareza de código e consistência acima de recursos mais complexos

Rust — complexa, mas poderosa em segurança e desempenho

  • Rust se apresenta com a ideia de “abstrações de custo zero”, sendo uma linguagem maximalista que combina vários conceitos
  • A alta dificuldade de aprendizado vem da densidade conceitual, com um sistema de tipos complexo e uma estrutura de traits elaborada
  • O objetivo central de Rust é conciliar desempenho e segurança de memória
    • Para evitar UB (Undefined Behavior), ela faz verificações em tempo de compilação
    • Isso bloqueia comportamentos imprevisíveis causados por referências de ponteiro inválidas, double free e problemas semelhantes
  • Para que o compilador consiga entender o comportamento do código em runtime, o desenvolvedor precisa definir explicitamente tipos e traits
  • Graças a essa estrutura, a confiabilidade no código de terceiros é alta, e o ecossistema de bibliotecas se mantém bastante ativo

Zig — controle total e design orientado a dados

  • Zig é a linguagem mais nova entre as três; está na versão 0.14 e quase não possui documentação da biblioteca padrão
  • Ela adota gerenciamento manual de memória, exigindo que o desenvolvedor chame alloc() diretamente e escolha o allocator
  • Diferentemente de Rust ou Go, criar variáveis globais é simples, e a linguagem detecta “illegal behavior” em runtime para interromper o programa
    • Com quatro modos de release selecionáveis no build, é possível equilibrar desempenho e estabilidade
  • Recursos de programação orientada a objetos (OOP) são excluídos intencionalmente
    • Não há campos private nem despacho dinâmico, e até std.mem.Allocator não é implementado como interface
    • Em vez disso, a linguagem favorece design orientado a dados (data-oriented design)
  • O gerenciamento de memória também recomenda, em vez do controle minucioso por objeto no estilo RAII, uma estrutura de alocar e liberar grandes blocos de memória periodicamente
  • Zig é descrita como uma linguagem livre e anti-establishment, que remove a forma de pensar orientada a OOP e maximiza o controle nas mãos do desenvolvedor
  • No momento, a equipe está focada em reescrever todas as dependências, e a versão estável (1.0) ainda não tem data definida

Conclusão — as diferenças de valores que as linguagens revelam

  • Go tem como valores centrais colaboração e simplicidade, Rust prioriza segurança e desempenho, e Zig enfatiza liberdade e controle
  • As diferenças entre as três não são apenas comparação de recursos, mas refletem uma escolha filosófica sobre desenvolvimento de software
  • O desenvolvedor acaba escolhendo a linguagem de acordo com os valores com que mais se identifica

1 comentários

 
GN⁺ 2025-12-06
Comentários do Hacker News
  • Em Rust, não é difícil criar uma variável global mutável
    Basta usar unsafe ou um smart pointer que ofereça sincronização
    Rust é reentrante por padrão e garante segurança entre threads em tempo de compilação
    Se você não se importa com segurança estática entre threads, dá para fazer isso tão facilmente quanto em Zig ou C
    A diferença é que Rust oferece mais ferramentas de garantia sobre o comportamento do código em tempo de execução

    • Como alguém que usa Rust há vários anos, acho que variável global mutável é um exemplo clássico de “só porque dá para fazer não significa que você deva”
      Quando volto para outras linguagens e vejo isso sendo usado com naturalidade, parece uma loucura do ponto de vista de segurança
    • Esse tipo de frase como “é trivial, só precisa de ~” eu já ouvi também sobre C++, Perl e Haskell
      Mas quando essas “coisinhas simples” vão se acumulando, deixam de ser simples
      Rust já passou dessa linha e agora definitivamente não é trivial
    • Fico curioso se o compilador de Rust detecta condições de corrida entre threads em tempo de compilação
      Se sim, isso parece bem mais atraente do que C
      Também queria saber como ele lida com situações em que duas variáveis precisam sempre ser travadas juntas
    • Se eu criasse uma linguagem, eu proibiria variáveis globais mutáveis por completo
      Ao depurar, no fim das contas o problema sempre acabava vindo daí
  • Sobre o texto que apontava a densidade conceitual de Rust, acho que na prática dá para ser produtivo conhecendo só 5% disso
    Uso Rust há mais de 12 anos e nunca precisei usar algo como #[fundamental]
    Dá para fazer arena allocation em Rust, e o conceito de allocator também existe
    Só existe um allocator padrão, e normalmente se usa alocação explícita no heap com algo como Box::new
    Dá para criar um global mutável como static FOO: Mutex<T> = Mutex::new(...), e o mutex é necessário para segurança de memória
    O sistema de tipos de Rust foi projetado para garantir não só segurança de memória, mas também segurança semântica do código

    • Mas como outros desenvolvedores podem usar outros 5~10% dos conceitos, em colaboração você acaba tendo que aprender mais conceitos
      Em C, há menos dessa complexidade
      Complexidade no fim é uma questão importante
    • Dizer que “Rust também permite arena allocation” é verdade, mas a maior parte do código Rust/Go tem como caminho padrão muitas alocações pequenas
      Não é só uma questão de ser possível ou não, mas de uma diferença no estilo básico de programação
    • Se o allocator em Rust é um tipo, fico curioso se no modelo de threads m:n dá para dar uma arena separada para cada requisição
    • Também perguntam se o allocator de Rust é global, e se todas as alocações no heap usam o mesmo allocator
    • Mencionam o vídeo sobre batch allocation do Casey Muratori, apontando que alguns desenvolvedores entendem isso errado e acabam criticando o RAII de Rust
      Também houve um caso em que a Zig Software Foundation citou incorretamente falas da Asahi Lina sobre Rust
      Essa postura de marketing do Zig de diminuir outras linguagens não agrada muito
  • O que agrada em Zig é que ela é uma linguagem capaz de lidar com falta de memória de forma elegante
    Toda alocação é assumida como falível, e isso precisa ser tratado explicitamente
    O espaço de stack também não é tratado de forma mágica; o compilador analisa o grafo de chamadas para inferir o tamanho máximo
    Em ambientes embarcados, esse tipo de design orientado a recursos é essencial

    • Mas em sistemas operacionais que usam overcommit, como Linux, na prática falha de alocação não acontece
      Isso não se resolve com tratamento no nível da linguagem
    • À pergunta sobre qual a razão de existir do Zig se Rust já existe, eu perguntaria antes “se C existe, por que Zig?”
      No fim, ambas encaram o mesmo problema de gerenciamento manual de memória
      Nesse caso, eu preferiria usar uma linguagem com GC
    • Fico curioso sobre como funciona a inferência de tamanho de stack do Zig quando há recursão ou chamadas por ponteiro de função
    • Zig não foi a primeira; vale olhar para a história das linguagens de sistema desde o JOVIAL em 1958
    • Rust também consegue lidar bem com pre-allocation
      Só que a biblioteca padrão de Rust usa panic em caso de OOM, então existe um ecossistema separado para desenvolvimento embarcado em ambiente no-std
  • O slice de Go é diferente do Vec<T> de Rust
    append() retorna um novo slice, que pode ou não compartilhar a memória existente
    Não há como reduzir a memória, e se você usar só append(s, ...), acabará ignorando o novo slice
    Go tem uma postura de “faça o que eu mandei”, enquanto Rust diz “verifique se você fez o que eu mandei”
    Ou seja, Go permite erros em nome da simplicidade, enquanto Rust escolhe o caminho de reduzir erros mesmo que isso traga mais complexidade

    • Na prática, dá para reduzir a memória com slices.Clip
      Além disso, usar apenas append(s, ...) gera erro de compilação, então o texto original faz uma afirmação um pouco imprecisa
      Go é uma linguagem que avalia com cuidado o aumento de complexidade ao adicionar funcionalidades
    • Como append(s, …) nem compila, acho que um iniciante nem teria como cometer esse erro
    • É interessante que, mesmo depois da chegada de genéricos em Go, tipos como List[T] não tenham se tornado amplamente usados
      Provavelmente porque não é tão comum precisar passar uma lista expansível diretamente
    • Em Go, a maioria das armadilhas (foot guns) está claramente descrita na especificação e na documentação
      Muitas vezes o problema é só a pessoa não ter lido a documentação e depois se surpreender
  • Acho difícil, na prática, capturar UB (Undefined Behavior) de C/C++ com verificações em tempo de execução
    O Android também aplicou sanitizers em todos os commits, mas só depois da migração para Rust os exploits diminuíram

    • Pedem a fonte dessa afirmação sobre os sanitizers no Android
  • Gostei que o texto comparando linguagens tratou com honestidade os pontos fortes e fracos de cada uma
    Só senti falta de Raku
    Na minha visão, se C–Zig–C++–Rust–Go formam um contínuo de linguagens de baixo nível, do lado de mais alto nível teríamos Julia–R–Python–Lua–JS–PHP–Raku–WL

    • Perguntam o que seria WL
    • Raku é uma linguagem de uso geral muito expressiva, com múltiplo despacho, roles, tipagem gradual, avaliação preguiçosa e um poderoso sistema de regex embutido
      Ela também oferece suporte à definição de gramática no nível da linguagem, o que facilita DSLs e parsing de logs
      Por ser baseada em VM, o desempenho é menor, mas ela é adequada para expressar diretamente a estrutura do problema
      Como sucessora do Perl, busca ser uma linguagem flexível e consistente
  • É um equívoco achar que, em Rust, quando uma função retorna um ponteiro isso gera automaticamente uma alocação no heap
    Variáveis locais ficam na stack e desaparecem ao retornar, então o ponteiro se torna inválido
    No modo seguro, Rust não permite dereferenciar ponteiros; no modo unsafe, o desenvolvedor assume a responsabilidade de garantir validade
    Provavelmente a pessoa confundiu Box::new com “alocação implícita”

    • É difícil entender a confusão entre a escape analysis de Go e a alocação explícita no heap em Rust
      Isso parece ou uma compreensão errada dos conceitos ou uma tentativa deliberada de induzir ao erro
  • A maior vantagem de Go é seu modelo simples de concorrência
    Com goroutines, dá para escrever código paralelo com facilidade

    • Uma das vantagens de Go é que, por causa da consistência do código, é fácil navegar por bases grandes
      Encontrar implementações de interfaces é difícil, mas a legibilidade ajuda muito na colaboração em equipe
    • A palestra “Concurrency is not Parallelism”, do Rob Pike, explica bem a filosofia de concorrência do Go
      Não há colored function, e a comunicação baseada em canais é simples, então dá para escrever código concorrente correto com rapidez
    • Mas acho que Structured Concurrency é um modelo ainda mais fácil
      Texto relacionado: Structured Concurrency or Go Statement Considered Harmful
    • A nova interface std.Io do Zig é parecida com o modelo de concorrência do Go
      A keyword go corresponde a std.Io.async, canais correspondem a std.Io.Queue, e select corresponde a std.Io.select
    • Um desenvolvedor de Erlang provavelmente não concordaria com a ideia de que o modelo de concorrência do Go é o mais fácil
  • O que eu queria era uma linguagem com a simplicidade do Go combinada com o tratamento de resultado/erro/enum do Rust e genéricos melhores

    • Também concordo. Há uma grande demanda de mercado por uma linguagem nativa com GC e um sistema de tipos mais poderoso
      Já vi OCaml, D, Swift, Nim e Crystal, mas nenhuma delas ainda dominou o mercado
    • Ouvi dizer que o OCaml moderno tem um modelo de concorrência muito forte e desempenho competitivo com o Go
    • A linguagem Borgo tentou algo nessa linha, mas foi descontinuada
      Em vez disso, talvez valha a pena olhar para Gleam
    • A proposta de errors do Go era interessante
      Espero que apareça alguma melhoria capaz de resolver esse problema recorrente
      Genéricos provavelmente ainda continuarão sendo um desafio
    • A alternativa mais próxima é o C#, mas ele continua sendo uma linguagem muito centrada em OOP
  • Gostei do tom geral do texto, dava para sentir o entusiasmo e a curiosidade de um desenvolvedor iniciante
    A ausência de genéricos em Go não foi só minimalismo simples, mas resultado de uma reflexão sobre trade-offs
    Os lifetimes de Rust foram a maior barreira para muita gente, e a inovação da linguagem está na combinação de conceitos já existentes
    O gerenciamento manual de memória em Zig se baseia mais na filosofia de Data-Oriented Design (DOD) do que na exclusão de OOP
    Palestra relacionada: apresentação sobre DOD do Andrew

    • Como no texto “The Generic Dilemma” de Russ Cox, de 2009,
      a questão central era “o que escolher entre programador lento, compilador lento ou execução lenta?”
      Ao que parece, a equipe do Go acabou encontrando um meio-termo satisfatório