Right off the bat, existem diferenças claras entre Go e Rust. Go tem um foco mais forte na construção de web APIs e pequenos serviços que podem escalar infinitamente, especialmente com o poder dos goroutinos. Este último também é possível com o Rust, mas as coisas são muito mais difíceis do ponto de vista da experiência do desenvolvedor.
Rust funciona bem para processar grandes quantidades de dados e outras operações de CPU intensiva, tais como a execução de algoritmos. Esta é a maior vantagem do Rust sobre Go. Projetos que exigem alto desempenho são geralmente mais adequados para Rust.
Neste tutorial, vamos comparar e contrastar Go e Rust, avaliando cada linguagem de programação para desempenho, simultaneidade, gerenciamento de memória, e a experiência geral do desenvolvedor. Também apresentaremos uma visão geral desses elementos para ajudá-lo a escolher a linguagem certa para o seu projeto em um relance.
Se você está apenas começando com Rust, pode ser uma boa idéia fazer um brush up neste guia para iniciantes antes de ler mais.
Se estás todo apanhado, vamos mergulhar!
Performance
Originalmente desenhado pelos engenheiros do Google, Go foi apresentado ao público em 2009. Foi criado para oferecer uma alternativa ao C++ que era mais fácil de aprender e codificar e foi optimizado para correr em CPUs com vários núcleos.
Desde então, Go tem sido óptimo para programadores que querem tirar partido da concorrência que o idioma oferece. A linguagem fornece goroutinos que permitem executar funções como subprocessos.
Uma grande vantagem do Go é a facilidade com que você pode usar goroutinos. Basta adicionar a sintaxe go
a uma função para que ela seja executada como subprocesso. O modelo de concorrência do Go permite que você implante cargas de trabalho em vários núcleos de CPU, tornando-o uma linguagem muito eficiente.
package mainimport ( "fmt" "time")func f(from string) { for i := 0; i < 3; i++ { fmt.Println(from, ":", i) }}func main() { f("direct") go f("goroutine") time.Sleep(time.Second) fmt.Println("done")}
Embora o suporte a CPU com vários núcleos, o Rust ainda consegue superar o Go. O Rust é mais eficiente na execução de algoritmos e operações com uso intensivo de recursos. O Benchmarks Game compara Rust e Go para vários algoritmos, tais como árvores binárias. Para todos os algoritmos testados, Rust foi pelo menos 30% mais rápido; no caso dos cálculos em árvores binárias, foi até 1.000 por cento. Um estudo realizado por Bitbucket mostra resultados similares nos quais Rust executa no mesmo nível de C++.
(Fonte: Benchmarks Game)
Concurrency
Como mencionado acima, Go suporta concorrência. Por exemplo, digamos que você esteja rodando um servidor web que lida com requisições de API. Você pode usar os goroutines do Go para executar cada requisição como um subprocesso, maximizando a eficiência ao descarregar tarefas para todos os núcleos de CPU disponíveis.
Goroutines são parte das funções internas do Go, enquanto o Rust só recebeu sintaxe nativa de async/await para suportar a concorrência. Como tal, a vantagem da experiência do desenvolvedor vai para Go quando se trata de concorrência. Entretanto, Rust é muito melhor em garantir a segurança da memória.
Aqui está um exemplo de threads simplificados para Rust:
use std::thread;use std::time::Duration;fn main() { // 1. create a new thread for i in 1..10 { thread::spawn(|| { println!("thread: number {}!", i); thread::sleep(Duration::from_millis(100)); }); } println!("hi from the main thread!");}
A moeda sempre foi um problema espinhoso para os desenvolvedores. Não é uma tarefa fácil garantir uma concorrência segura para a memória sem comprometer a experiência do desenvolvedor. No entanto, este foco extremo de segurança levou à criação de uma concorrência comprovadamente correta. Rust experimentou o conceito de propriedade para evitar o acesso não solicitado de recursos para evitar bugs de segurança de memória.
Rust oferece quatro paradigmas diferentes de concorrência para ajudá-lo a evitar as armadilhas comuns de segurança de memória. Vamos analisar mais de perto dois paradigmas comuns: channel e lock.
Channel
A channel ajuda a transferir uma mensagem de um thread para outro. Enquanto este conceito também existe para Go, Rust permite transferir um ponteiro de um thread para outro para evitar condições de corrida por recursos. Através de ponteiros de passagem, o Rust pode impor o isolamento de threads para os canais. Novamente, Rust mostra sua obsessão com a segurança da memória em relação ao seu modelo concorrente.
Lock
Data só é acessível quando a trava é mantida. Rust se baseia no princípio de travamento de dados ao invés de código, que é frequentemente encontrado em linguagens de programação como Java.
Para mais detalhes sobre o conceito de propriedade e todos os paradigmas de concorrência, verifique “Fearless Concurrency with Rust”
Segurança da memória
O conceito anterior de propriedade é um dos principais pontos de venda de Rust. Rust leva a segurança do tipo, que também é importante para permitir uma concorrência segura para a memória, para o próximo nível.
De acordo com o blog Bitbucket, “O compilador muito rígido e pedante de Rust verifica cada variável que você usa e cada endereço de memória que você refere. Ele evita possíveis condições de corrida de dados e o informa sobre comportamento indefinido”
Isso significa que você não vai acabar com um buffer overflow ou uma condição de corrida devido à obsessão extrema do Rust com a segurança da memória. No entanto, isto também tem as suas desvantagens. Por exemplo, você tem que estar atento aos princípios de alocação de memória enquanto escreve código. Não é fácil ter sempre a sua guarda de segurança de memória levantada.
A experiência do desenvolvedor
Primeiro de tudo, vamos olhar para a curva de aprendizagem associada a cada língua. Go foi projetado com a simplicidade em mente. Os desenvolvedores frequentemente se referem a ele como uma linguagem “chata”, ou seja, seu conjunto limitado de recursos incorporados torna Go fácil de adotar.
Outras vezes, Go oferece uma alternativa mais fácil ao C++, ocultando aspectos como segurança e alocação de memória. O Rust adota outra abordagem, forçando-o a pensar em conceitos como segurança da memória. O conceito de propriedade e a capacidade de passar ponteiros faz de Rust uma opção menos atraente para aprender. Quando você está constantemente pensando na segurança da memória, você é menos produtivo e o seu código é obrigado a ser mais comlex.
A curva de aprendizagem para Rust é bastante íngreme em comparação com Go. Vale a pena mencionar, no entanto, que Go tem uma curva de aprendizado mais íngreme que linguagens mais dinâmicas como Python e JavaScript.
Quando usar Go
Go funciona bem para uma grande variedade de casos de uso, tornando-o uma ótima alternativa ao Node.js para a criação de APIs web. Como notado por Loris Cro, “O modelo de concorrência de Go é um bom ajuste para aplicações do lado do servidor que devem lidar com múltiplas requisições independentes”. É exatamente por isso que Go fornece goroutines.
O que é mais, Go tem suporte embutido para o protocolo web HTTP. Você pode projetar rapidamente uma pequena API usando o suporte HTTP embutido e executá-la como um microserviço. Portanto, Go se encaixa bem na arquitetura de microserviços e atende às necessidades dos desenvolvedores de API.
Em resumo, Go é um bom ajuste se você valoriza a velocidade de desenvolvimento e prefere a simplicidade de sintaxe ao invés do desempenho. Além disso, Go oferece melhor legibilidade do código, o que é um critério importante para grandes equipes de desenvolvimento.
Selecionar Go quando:
- Você se importa com simplicidade e legibilidade
- Você quer uma sintaxe fácil para escrever código rapidamente
- Você quer usar uma linguagem mais flexível que suporte o desenvolvimento web
Quando usar Rust
Rust é uma ótima escolha quando a performance é importante, como quando você está processando grandes quantidades de dados. Além disso, o Rust dá-lhe um controlo de granulação fina sobre como os threads se comportam e como os recursos são partilhados entre threads.
Por outro lado, o Rust vem com uma curva de aprendizagem íngreme e reduz a velocidade de desenvolvimento devido à complexidade extra da segurança da memória. Isto não é necessariamente uma desvantagem; Rust também garante que você não encontrará bugs de segurança de memória enquanto o compilador verifica todos e cada um dos apontadores de dados. Para sistemas complexos, esta garantia pode ser útil.
Selecionar Rust quando:
- Você se importa com o desempenho
- Você quer um controle fino sobre os threads
- Você valoriza a segurança da memória acima da simplicidade
Vá vs. Rust: A minha opinião honesta
Vamos começar por realçar as semelhanças. Tanto Go como Rust são de código aberto e projetados para suportar a arquitetura de microserviços e ambientes de computação paralela. Ambos otimizam a utilização dos núcleos de CPU disponíveis através da concorrência.
Mas no final do dia, qual é a melhor linguagem?
Existem muitas maneiras de abordar esta questão. Eu recomendaria pensar sobre que tipo de aplicação você quer construir. Go serve bem para criar aplicações web e APIs que tirem proveito de seus recursos de concorrência embutidos enquanto suportam a arquitetura de microserviços.
Você também pode usar o Rust para desenvolver uma API web, mas ele não foi projetado com este caso de uso em mente. O foco do Rust na segurança da memória aumenta a complexidade e o tempo de desenvolvimento, especialmente para uma API web bastante simples. Entretanto, a maior quantidade de controle que você tem sobre seu código permite que você escreva código mais otimizado, eficiente na memória e performante.
Para colocar da forma mais simples possível, o debate Go versus Rust é realmente uma questão de simplicidade versus segurança.
Para mais perspectivas, confira “Escolhendo entre Go e Rust”.
LogRocket: Total visibilidade das aplicações Rust de produção
Depuração das aplicações Rust podem ser difíceis, especialmente quando os utilizadores experimentam problemas que são difíceis de reproduzir. Se você está interessado em monitorar e rastrear o desempenho dos seus aplicativos Rust, automaticamente surfacing erros, e rastrear pedidos de rede lentos e tempo de carga, tente LogRocket.
LogRocket é como um DVR para aplicações web, gravando literalmente tudo o que acontece no seu aplicativo Rust. Ao invés de adivinhar por que os problemas acontecem, você pode agregar e relatar em que estado sua aplicação estava quando um problema ocorreu. O LogRocket também monitora o desempenho do seu aplicativo, reportando métricas como carga de CPU do cliente, uso de memória do cliente e mais.
Modernize como você depura seus aplicativos Rust – comece a monitorar gratuitamente.