- 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
Comentários do Hacker News
Em Rust, não é difícil criar uma variável global mutável
Basta usar
unsafeou um smart pointer que ofereça sincronizaçãoRust é 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
Quando volto para outras linguagens e vejo isso sendo usado com naturalidade, parece uma loucura do ponto de vista de segurança
Mas quando essas “coisinhas simples” vão se acumulando, deixam de ser simples
Rust já passou dessa linha e agora definitivamente não é trivial
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
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::newDá para criar um global mutável como
static FOO: Mutex<T> = Mutex::new(...), e o mutex é necessário para segurança de memóriaO 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
Em C, há menos dessa complexidade
Complexidade no fim é uma questão importante
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
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
Isso não se resolve com tratamento no nível da linguagem
No fim, ambas encaram o mesmo problema de gerenciamento manual de memória
Nesse caso, eu preferiria usar uma linguagem com GC
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 Rustappend()retorna um novo slice, que pode ou não compartilhar a memória existenteNão há como reduzir a memória, e se você usar só
append(s, ...), acabará ignorando o novo sliceGo 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
Além disso, usar apenas
append(s, ...)gera erro de compilação, então o texto original faz uma afirmação um pouco imprecisaGo é uma linguagem que avalia com cuidado o aumento de complexidade ao adicionar funcionalidades
append(s, …)nem compila, acho que um iniciante nem teria como cometer esse erroProvavelmente porque não é tão comum precisar passar uma lista expansível diretamente
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
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
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::newcom “alocação implícita”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
Encontrar implementações de interfaces é difícil, mas a legibilidade ajuda muito na colaboração em equipe
Não há colored function, e a comunicação baseada em canais é simples, então dá para escrever código concorrente correto com rapidez
Texto relacionado: Structured Concurrency or Go Statement Considered Harmful
std.Iodo Zig é parecida com o modelo de concorrência do GoA keyword
gocorresponde astd.Io.async, canais correspondem astd.Io.Queue, eselectcorresponde astd.Io.selectO 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
Já vi OCaml, D, Swift, Nim e Crystal, mas nenhuma delas ainda dominou o mercado
Em vez disso, talvez valha a pena olhar para Gleam
Espero que apareça alguma melhoria capaz de resolver esse problema recorrente
Genéricos provavelmente ainda continuarão sendo um desafio
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
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