---
title: "Rust para Programadores Go: Transição | Rust Brasil"
url: "https://rustlang.com.br/blog/rust-para-programadores-go/"
markdown_url: "https://rustlang.com.br/blog/rust-para-programadores-go.MD"
description: "Guia de transição de Go para Rust: concorrência, tratamento de erros, tipos e ferramentas comparados lado a lado."
date: "2026-02-16"
author: "Equipe Rust Brasil"
---

# Rust para Programadores Go: Transição | Rust Brasil

Guia de transição de Go para Rust: concorrência, tratamento de erros, tipos e ferramentas comparados lado a lado.


Go e Rust são frequentemente comparados como linguagens modernas de sistemas. Ambas compilam para código nativo, possuem excelente ferramental e focam em desempenho. Se você já programa em Go e quer aprender Rust, este guia mapeia os conceitos que você já conhece para seus equivalentes em Rust, destacando as diferenças fundamentais de design entre as duas linguagens.

## Visão Geral: Filosofias Diferentes

| Aspecto | Go | Rust |
|---|---|---|
| Gerenciamento de memória | Garbage Collector | Ownership + Borrow Checker |
| Concorrência | Goroutines + channels | async/await + threads + channels |
| Tratamento de erros | `error` interface, `if err != nil` | `Result<T, E>`, operador `?` |
| Polimorfismo | Interfaces (implícitas) | Traits (explícitas) |
| Genéricos | Sim (desde Go 1.18) | Sim (desde o início) |
| Null safety | `nil` (pode causar panic) | `Option<T>` (verificado em compilação) |

## Variáveis e Tipos Básicos

As duas linguagens usam tipagem estática, mas Rust é mais rigoroso com mutabilidade e oferece mais tipos numéricos.

| Conceito | Go | Rust |
|---|---|---|
| Variável | `var x int = 10` | `let x: i32 = 10;` |
| Inferência | `x := 10` | `let x = 10;` |
| Mutável | Todas são mutáveis | `let mut x = 10;` |
| Constante | `const Pi = 3.14` | `const PI: f64 = 3.14;` |
| Zero value | Sim (ex: `0`, `""`, `nil`) | Sem zero values |

**Go:**
```go
package main

import "fmt"

func main() {
    nome := "Rust Brasil"
    idade := 5
    ativo := true
    fmt.Printf("%s tem %d anos, ativo: %t\n", nome, idade, ativo)
}
```

**Rust:**
```rust
fn main() {
    let nome = "Rust Brasil";
    let idade = 5;
    let ativo = true;
    println!("{} tem {} anos, ativo: {}", nome, idade, ativo);
}
```

A diferença fundamental: em Go, todas as variáveis são mutáveis. Em Rust, variáveis são imutáveis por padrão e você deve optar explicitamente pela mutabilidade com `mut`.

## Structs

Ambas as linguagens usam structs, mas Rust não tem campos exportados por letra maiúscula -- usa `pub` em vez disso.

**Go:**
```go
type Usuario struct {
    Nome  string
    Email string
    Idade int
}

func NovoUsuario(nome, email string, idade int) Usuario {
    return Usuario{
        Nome:  nome,
        Email: email,
        Idade: idade,
    }
}

func (u Usuario) Saudacao() string {
    return fmt.Sprintf("Olá, meu nome é %s", u.Nome)
}
```

**Rust:**
```rust
pub struct Usuario {
    pub nome: String,
    pub email: String,
    pub idade: u32,
}

impl Usuario {
    pub fn novo(nome: &str, email: &str, idade: u32) -> Self {
        Usuario {
            nome: nome.to_string(),
            email: email.to_string(),
            idade,
        }
    }

    pub fn saudacao(&self) -> String {
        format!("Olá, meu nome é {}", self.nome)
    }
}
```

## Interfaces vs Traits

Go usa interfaces implícitas -- se um tipo tem os métodos certos, ele implementa a interface automaticamente. Rust usa traits com implementação explícita.

**Go:**
```go
type Falante interface {
    Falar() string
}

type Cachorro struct {
    Nome string
}

// Implementação implícita -- não há declaração "implements"
func (c Cachorro) Falar() string {
    return c.Nome + " diz: Au au!"
}

type Gato struct {
    Nome string
}

func (g Gato) Falar() string {
    return g.Nome + " diz: Miau!"
}

func fazerFalar(f Falante) {
    fmt.Println(f.Falar())
}
```

**Rust:**
```rust
trait Falante {
    fn falar(&self) -> String;
}

struct Cachorro {
    nome: String,
}

// Implementação explícita
impl Falante for Cachorro {
    fn falar(&self) -> String {
        format!("{} diz: Au au!", self.nome)
    }
}

struct Gato {
    nome: String,
}

impl Falante for Gato {
    fn falar(&self) -> String {
        format!("{} diz: Miau!", self.nome)
    }
}

fn fazer_falar(f: &dyn Falante) {
    println!("{}", f.falar());
}

// Ou com generics (despacho estático, mais performático)
fn fazer_falar_generico<T: Falante>(f: &T) {
    println!("{}", f.falar());
}
```

Rust oferece duas formas de polimorfismo: despacho dinâmico com `dyn Trait` (semelhante a interfaces Go) e despacho estático com generics (sem custo em runtime).

## Tratamento de Erros: `if err != nil` vs `Result<T, E>`

Este é um dos maiores contrastes. Go usa o padrão `(valor, error)` com checagem manual. Rust usa o tipo `Result<T, E>` com o operador `?`.

**Go:**
```go
import (
    "fmt"
    "os"
    "strconv"
)

func lerArquivoComoNumero(caminho string) (int, error) {
    dados, err := os.ReadFile(caminho)
    if err != nil {
        return 0, fmt.Errorf("erro ao ler arquivo: %w", err)
    }

    numero, err := strconv.Atoi(string(dados))
    if err != nil {
        return 0, fmt.Errorf("erro ao converter: %w", err)
    }

    return numero, nil
}

func main() {
    num, err := lerArquivoComoNumero("numero.txt")
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }
    fmt.Println("Número:", num)
}
```

**Rust:**
```rust
use std::fs;
use std::num::ParseIntError;
use std::io;

#[derive(Debug)]
enum MeuErro {
    Io(io::Error),
    Parse(ParseIntError),
}

impl From<io::Error> for MeuErro {
    fn from(e: io::Error) -> Self { MeuErro::Io(e) }
}

impl From<ParseIntError> for MeuErro {
    fn from(e: ParseIntError) -> Self { MeuErro::Parse(e) }
}

fn ler_arquivo_como_numero(caminho: &str) -> Result<i32, MeuErro> {
    let dados = fs::read_to_string(caminho)?;  // propaga erro de IO
    let numero: i32 = dados.trim().parse()?;    // propaga erro de parse
    Ok(numero)
}

fn main() {
    match ler_arquivo_como_numero("numero.txt") {
        Ok(num) => println!("Número: {}", num),
        Err(e) => println!("Erro: {:?}", e),
    }
}
```

O operador `?` em Rust elimina o repetitivo `if err != nil` do Go. Na prática, muitos projetos Rust usam a crate `anyhow` ou `thiserror` para simplificar ainda mais o tratamento de erros.

## `nil` vs `Option<T>`

Go usa `nil` para ponteiros, slices, maps, channels e interfaces. Isso pode causar panics em runtime. Rust usa `Option<T>`, que é verificado em tempo de compilação.

**Go:**
```go
func buscarUsuario(id int) *Usuario {
    if id == 1 {
        return &Usuario{Nome: "Ana"}
    }
    return nil  // pode causar panic se não verificado
}

func main() {
    u := buscarUsuario(2)
    if u != nil {
        fmt.Println(u.Nome)
    }
    // Se esquecer o check: panic: nil pointer dereference
}
```

**Rust:**
```rust
fn buscar_usuario(id: u32) -> Option<Usuario> {
    if id == 1 {
        Some(Usuario::novo("Ana", "ana@email.com", 30))
    } else {
        None
    }
}

fn main() {
    // O compilador OBRIGA o tratamento
    match buscar_usuario(2) {
        Some(u) => println!("{}", u.nome),
        None => println!("Usuário não encontrado"),
    }

    // Ou com if let
    if let Some(u) = buscar_usuario(1) {
        println!("{}", u.nome);
    }

    // Ou com unwrap_or
    let nome = buscar_usuario(2)
        .map(|u| u.nome)
        .unwrap_or_else(|| String::from("Desconhecido"));
}
```

## Goroutines vs Async/Threads

Go tem goroutines integradas na linguagem. Rust oferece threads nativas do SO e async/await com um runtime como Tokio.

**Go (goroutines + channels):**
```go
func main() {
    ch := make(chan string, 2)

    go func() {
        time.Sleep(1 * time.Second)
        ch <- "resultado 1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "resultado 2"
    }()

    fmt.Println(<-ch)
    fmt.Println(<-ch)
}
```

**Rust (async com Tokio):**
```rust
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel(2);

    let tx1 = tx.clone();
    tokio::spawn(async move {
        sleep(Duration::from_secs(1)).await;
        tx1.send("resultado 1").await.unwrap();
    });

    tokio::spawn(async move {
        sleep(Duration::from_secs(2)).await;
        tx.send("resultado 2").await.unwrap();
    });

    while let Some(msg) = rx.recv().await {
        println!("{}", msg);
    }
}
```

**Rust (threads nativas + mpsc):**
```rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();

    let tx1 = tx.clone();
    thread::spawn(move || {
        thread::sleep(Duration::from_secs(1));
        tx1.send("resultado 1").unwrap();
    });

    thread::spawn(move || {
        thread::sleep(Duration::from_secs(2));
        tx.send("resultado 2").unwrap();
    });

    for msg in rx {
        println!("{}", msg);
    }
}
```

| Conceito | Go | Rust |
|---|---|---|
| Concorrência leve | `go func()` | `tokio::spawn(async { })` |
| Threads do SO | `go func()` (com GOMAXPROCS) | `std::thread::spawn` |
| Channels | `make(chan T)` | `mpsc::channel()` / `tokio::sync::mpsc` |
| Mutex | `sync.Mutex` | `std::sync::Mutex<T>` |
| WaitGroup | `sync.WaitGroup` | `tokio::task::JoinSet` / `join!` |

Uma diferença importante: o `Mutex<T>` do Rust encapsula os dados protegidos, tornando impossível acessar os dados sem adquirir o lock. Em Go, o mutex e os dados são separados, o que depende da disciplina do programador.

## Slices vs Vec e Slices em Rust

Go tem slices como tipo fundamental. Rust separa `Vec<T>` (alocado no heap, growable) e slices `&[T]` (referência a uma sequência).

**Go:**
```go
nums := []int{1, 2, 3, 4, 5}
nums = append(nums, 6)
fatia := nums[1:3]  // [2, 3]

// Iterar
for i, v := range nums {
    fmt.Printf("índice %d: valor %d\n", i, v)
}
```

**Rust:**
```rust
let mut nums = vec![1, 2, 3, 4, 5];
nums.push(6);
let fatia = &nums[1..3];  // [2, 3]

// Iterar
for (i, v) in nums.iter().enumerate() {
    println!("índice {}: valor {}", i, v);
}
```

## Sistema de Pacotes: `go mod` vs Cargo

| Tarefa | Go | Rust |
|---|---|---|
| Iniciar projeto | `go mod init modulo` | `cargo new projeto` |
| Arquivo de config | `go.mod` | `Cargo.toml` |
| Lock file | `go.sum` | `Cargo.lock` |
| Adicionar dependência | `go get pacote@versão` | `cargo add pacote` |
| Compilar | `go build` | `cargo build` |
| Executar | `go run .` | `cargo run` |
| Testar | `go test ./...` | `cargo test` |
| Formatar código | `gofmt` | `rustfmt` / `cargo fmt` |
| Linter | `golangci-lint` | `cargo clippy` |
| Documentação | `godoc` | `cargo doc` |
| Repositório central | proxy.golang.org | crates.io |

**Go (`go.mod`):**
```
module github.com/usuario/projeto

go 1.21

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/lib/pq v1.10.9
)
```

**Rust (`Cargo.toml`):**
```toml
[package]
name = "projeto"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4"
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }
```

Cargo é mais integrado que o ferramental Go: compilação, testes, documentação, formatação, linting e gerenciamento de dependências estão todos sob um único comando.

## Ponteiros vs Referências

Go usa ponteiros (`*T`) com semântica simples. Rust usa referências (`&T`, `&mut T`) com o sistema de ownership.

**Go:**
```go
func incrementar(x *int) {
    *x++
}

func main() {
    valor := 10
    incrementar(&valor)
    fmt.Println(valor) // 11
}
```

**Rust:**
```rust
fn incrementar(x: &mut i32) {
    *x += 1;
}

fn main() {
    let mut valor = 10;
    incrementar(&mut valor);
    println!("{}", valor); // 11
}
```

A regra fundamental do Rust: você pode ter muitas referências imutáveis (`&T`) OU uma referência mutável (`&mut T`) -- nunca ambas ao mesmo tempo. Isso previne data races em tempo de compilação.

## Enums: O Superpoder do Rust

Go não tem enums no mesmo sentido que Rust. Go usa `iota` para constantes enumeradas. Rust tem enums algébricos que podem carregar dados.

**Go:**
```go
type Status int

const (
    Pendente Status = iota
    EmProgresso
    Concluido
)

// Para dados associados, precisa de struct separada
type Resultado struct {
    Sucesso bool
    Valor   string
    Erro    string
}
```

**Rust:**
```rust
enum Status {
    Pendente,
    EmProgresso,
    Concluido,
}

// Enums podem carregar dados diferentes em cada variante
enum Resultado {
    Sucesso(String),
    Erro { codigo: u32, mensagem: String },
    Carregando,
}

fn processar(r: Resultado) {
    match r {
        Resultado::Sucesso(valor) => println!("OK: {}", valor),
        Resultado::Erro { codigo, mensagem } => {
            println!("Erro {}: {}", codigo, mensagem)
        }
        Resultado::Carregando => println!("Aguarde..."),
    }
}
```

Os tipos `Option<T>` e `Result<T, E>` do Rust são, na verdade, enums comuns da biblioteca padrão. Esse recurso substitui muitos padrões que em Go exigem structs, interfaces ou múltiplos valores de retorno.

## Conclusão

Go e Rust compartilham objetivos de desempenho e segurança, mas com filosofias distintas. Go prioriza simplicidade e facilidade de aprendizado. Rust prioriza garantias em tempo de compilação e controle sobre a memória.

**Dicas para a transição:**

1. **Ownership e borrowing** -- esse é o maior salto conceitual vindo de Go. O garbage collector não existe em Rust; em vez disso, o compilador verifica o uso de memória em tempo de compilação.
2. **Enums algébricos** -- aproveite esse recurso. Ele substitui muitos padrões que em Go exigem múltiplos tipos ou interfaces.
3. **O operador `?`** -- quando dominar `Result` e `?`, você nunca mais vai sentir falta de `if err != nil`.
4. **Cargo é seu melhor amigo** -- ele integra tudo que em Go está espalhado entre `go build`, `go test`, `gofmt`, `golint`, etc.
5. **Use `clippy` desde o início** -- `cargo clippy` é essencial para aprender Rust idiomático.

A transição de Go para Rust é uma das mais suaves entre linguagens, graças às semelhanças em filosofia: ambas favorecem explicitação, composição sobre herança e ferramental integrado. Bem-vindo ao Rust!

Se você quer manter seus conhecimentos em Go atualizados enquanto aprende Rust, visite o [Go Brasil](https://golang.com.br) — nosso portal irmão com tutoriais, glossário e vagas de Go em português.
