Rust ou Go para o seu backend: como escolhemos
Ambas são modernas, rápidas e compiladas. A verdadeira questão não é qual é «melhor», mas qual se adequa ao problema que tem pela frente. Eis como decidimos na prática.
Construímos sistemas de produção tanto em Rust como em Go, e o nosso próprio produto assenta nas duas: Go onde a velocidade de iteração conta, Rust onde a latência e a segurança de memória não são negociáveis. Por isso, quando nos perguntam que linguagem usar num novo backend, a nossa resposta é: depende, e quem lhe der uma resposta de uma só palavra está a tentar vender-lhe alguma coisa.
Este artigo é a comparação que gostaríamos que mais pessoas fizessem. Deixa de lado o duelo de benchmarks e olha para os compromissos práticos que decidem a escolha em projetos reais.
O que têm em comum
Comecemos pelo que as une, porque é muito. Ambas compilam para código nativo, ambas são suficientemente rápidas para que a linguagem raramente seja o seu estrangulamento, ambas geram um único binário estático, trivial de implementar num contentor, e ambas oferecem concorrência de primeira linha e um ecossistema saudável. Qualquer uma é uma escolha defensável para um serviço de backend. Vai arrepender-se de escolher a «errada» muito menos do que as discussões na internet sugerem.
Onde o Go ganha
O Go otimiza sobretudo uma coisa: tornar uma equipa produtiva e mantê-la assim. A linguagem é pequena o suficiente para se aprender numa semana, a biblioteca padrão cobre quase tudo o que um serviço precisa e as goroutines tornam o código concorrente acessível em vez de assustador. A compilação é quase instantânea, o que mantém curto o ciclo de feedback.
- Serviços de backend comuns: APIs REST e gRPC, microsserviços, serviços de orquestração, workers.
- Equipas que precisam de avançar depressa e de integrar rapidamente novos engenheiros.
- Serviços de rede com muita concorrência, onde as goroutines brilham e o runtime faz a parte difícil.
- Tudo aquilo em que a simplicidade e a legibilidade importam mais do que espremer os últimos pontos percentuais de desempenho.
O custo é que o Go tem recolha de lixo. Para a esmagadora maioria dos serviços, isso não é problema, porque o coletor é excelente. Mas implica pausas ocasionais e menos controlo sobre a disposição da memória, o que conta para uma classe específica de trabalho sensível à latência.
Onde o Rust ganha
O Rust dá-lhe desempenho ao nível do C, com segurança de memória e de threads garantida em tempo de compilação, e sem coletor de lixo. O borrow checker que frustra os principiantes é o mesmo mecanismo que torna impossível publicar categorias inteiras de erros: use-after-free, corridas de dados, desreferências de ponteiros nulos. Quando a correção e a velocidade importam por igual, essa garantia vale uma curva de aprendizagem mais íngreme.
- Software de sistemas e de rede: protocolos, proxies, agentes, o plano de dados.
- Pipelines em tempo real em que uma pausa do coletor de lixo é inaceitável, como os media em direto.
- Componentes críticos para o desempenho dentro de um sistema maior, muitas vezes via FFI ou como um serviço focado.
- WebAssembly, onde o Rust é um cidadão de primeira classe para levar ao navegador código com velocidade nativa.
O custo é real: o Rust demora mais a aprender, mais a escrever e mais a compilar. Uma equipa que se estreia com ele será mais lenta nos primeiros meses. No domínio certo, esse investimento compensa; no errado, é puro custo adicional.
Como escolhemos na prática
Na nossa própria plataforma, a divisão surgiu naturalmente: Go onde a velocidade de iteração e a legibilidade são o que mais conta, Rust onde a latência e a segurança de memória não são negociáveis. Não escolhemos um lado; ajustámos a ferramenta à tarefa.
Recorra ao Rust quando uma falha, uma corrida de dados ou uma pausa do coletor forem realmente inaceitáveis. Para quase tudo o resto, use Go. A maioria dos backends é «tudo o resto».
Uma boa regra por omissão: comece um novo backend em Go, a não ser que tenha uma razão concreta para não o fazer, e introduza Rust de forma cirúrgica nos componentes que realmente precisam dele. Não tem de escolher uma única linguagem para todo o sistema. Um serviço em Go com um componente em Rust atrás de uma FFI, ou um pequeno serviço em Rust atrás de uma API em Go, é uma arquitetura perfeitamente sensata.
Se está a ponderar esta escolha para um projeto real e quer uma segunda opinião de quem publica software nas duas linguagens, é exatamente o tipo de conversa que teremos todo o gosto em ter.