Rust: Do Zero ao Júnior

Roadmap profissional e didático para dominar Rust. Aprenda desde o básico até projetos práticos com explicações claras.

18 Módulos 50+ Exercícios 5 Projetos Zero → Júnior

Índice do Roadmap

01

Introdução à Programação e ao Rust

O que é programação?

Programar é como ensinar um computador a executar tarefas, assim como ensinar alguém a seguir uma receita de bolo. Você fornece instruções detalhadas e o computador as executa exatamente como foram dadas.

Por que aprender Rust?

Segurança de Memória: Rust previne bugs como null pointer dereference e data races em tempo de compilação.

Performance: Velocidade comparável a C/C++ sem garbage collector.

Produtividade: Mensagens de erro claras e excelente tooling.

Mercado: Rust é a linguagem mais amada por 8 anos consecutivos no Stack Overflow.

Comparação com outras linguagens

CaracterísticaRustPythonC/C++
VelocidadeMuito rápidoLentoMuito rápido
Segurança de memóriaGarantida em compilaçãoGarbage CollectorManual (propenso a erros)
Curva de aprendizadoModeradaFácilDifícil
AplicaçõesSistemas, Web, CLIScripts, ML, WebSistemas, Games
02

Primeiros Passos

Instalando o Rust

Windows (Passo a Passo)

  1. Instale o Visual Studio Build Tools (necessário para compilar):
    • Acesse Visual Studio Build Tools
    • Baixe e execute o instalador
    • Selecione "Desenvolvimento para desktop com C++"
    • Clique em Instalar e aguarde
  2. Baixe o instalador do Rust:
    • Acesse rustup.rs
    • Clique em "rustup-init.exe (64-bit)"
  3. Execute o instalador:
    • Abra o arquivo rustup-init.exe baixado
    • Pressione 1 e Enter para instalação padrão
    • Aguarde o download e instalação
  4. Reinicie o terminal (ou abra um novo) para carregar as variáveis de ambiente
powershell
# Verificar instalação no PowerShell ou CMD
rustc --version
cargo --version

Linux/Mac

bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Verificando a instalação

bash
rustc --version
cargo --version

Seu primeiro programa

rust
fn main() {
    println!("Olá, mundo!");
}

O println! é uma macro (note o !), não uma função. Macros em Rust podem fazer coisas que funções não podem, como aceitar número variável de argumentos. É por isso que usamos println! com exclamação!

Usando Cargo

Cargo é o gerenciador de pacotes e build system do Rust:

bash
# Criar novo projeto
cargo new meu_projeto
cd meu_projeto

# Compilar e executar
cargo run

# Apenas compilar
cargo build

# Compilar para produção (otimizado)
cargo build --release
03

Fundamentos de Programação

Variáveis e Mutabilidade

Em Rust, variáveis são imutáveis por padrão. Isso ajuda a prevenir bugs.

rust
// Variável imutável (padrão)
let idade: i32 = 30;
let nome = "Maria";
let ativo = true;

// Variável mutável - pode ser alterada
let mut contador = 0;
contador = 1; // OK!

// Constantes - sempre imutáveis, definidas em tempo de compilação
const MAX_PONTOS: u32 = 100_000;

Tipos de Dados

TipoDescriçãoExemplo
i8, i16, i32, i64, i128Inteiros com sinallet x: i32 = -42;
u8, u16, u32, u64, u128Inteiros sem sinallet x: u32 = 42;
f32, f64Ponto flutuantelet pi: f64 = 3.14;
boolBooleanolet ativo = true;
charCaractere Unicodelet letra = 'A';
StringString dinâmicalet s = String::from("olá");
&strString slicelet s = "olá";

Operadores

rust
// Aritméticos
let soma = 5 + 3;        // 8
let sub = 10 - 4;        // 6
let mult = 3 * 4;        // 12
let div = 10 / 3;        // 3 (divisão inteira)
let resto = 10 % 3;      // 1

// Comparação
let igual = 5 == 5;      // true
let diferente = 5 != 3;  // true
let maior = 10 > 5;      // true
let menor_igual = 5 <= 5; // true

// Lógicos
let e = true && false;   // false
let ou = true || false;  // true
let nao = !true;         // false

Exercício: Crie variáveis para armazenar seu nome, idade e se você gosta de programação. Imprima tudo usando println!.

04

Controle de Fluxo

Condicionais (if/else)

rust
let idade = 18;

// if simples
if idade >= 18 {
    println!("Maior de idade");
} else {
    println!("Menor de idade");
}

// if else if
let nota = 75;
if nota >= 90 {
    println!("A");
} else if nota >= 70 {
    println!("B");
} else {
    println!("C");
}

// if como expressão (retorna valor!)
let status = if idade >= 18 { "adulto" } else { "menor" };
println!("Status: {}", status);

Match (Pattern Matching)

O match é uma das features mais poderosas do Rust:

rust
let dia = 3;

match dia {
    1 => println!("Segunda"),
    2 => println!("Terça"),
    3 => println!("Quarta"),
    4 | 5 => println!("Quinta ou Sexta"), // múltiplos padrões
    6..=7 => println!("Fim de semana"),    // range inclusivo
    _ => println!("Dia inválido"),         // curinga (obrigatório!)
}

// match com retorno
let dia_nome = match dia {
    1 => "Segunda",
    2 => "Terça",
    _ => "Outro dia",
};

Loops

rust
// loop infinito (use break para sair)
let mut contador = 0;
loop {
    contador += 1;
    if contador == 5 {
        break;
    }
}

// while
let mut n = 3;
while n > 0 {
    println!("{}!", n);
    n -= 1;
}
println!("Lançar!");

// for com range
for i in 1..5 {        // 1, 2, 3, 4 (exclusivo)
    println!("{}", i);
}

for i in 1..=5 {       // 1, 2, 3, 4, 5 (inclusivo)
    println!("{}", i);
}

// for com iterador
let nums = [10, 20, 30];
for num in nums.iter() {
    println!("{}", num);
}

Exercício: Crie um programa que verifica se um número é par ou ímpar usando if e outro usando match.

05

Funções

Declarando Funções

rust
// Função simples
fn saudar() {
    println!("Olá!");
}

// Função com parâmetros
fn saudar_pessoa(nome: &str) {
    println!("Olá, {}!", nome);
}

// Função com retorno
fn soma(a: i32, b: i32) -> i32 {
    a + b  // sem ponto-e-vírgula = retorno implícito
}

// Retorno explícito
fn dobro(x: i32) -> i32 {
    return x * 2;  // com return, precisa de ;
}

fn main() {
    saudar();
    saudar_pessoa("Maria");
    let resultado = soma(5, 3);
    println!("Soma: {}", resultado);
}

Em Rust, a última expressão de uma função (sem ;) é retornada automaticamente. Isso é chamado de "retorno implícito" e é o estilo idiomático.

Expressões vs Statements

rust
fn exemplo() -> i32 {
    let x = 5;          // statement (não retorna valor)

    let y = {           // bloco é uma expressão!
        let temp = 3;
        temp + 1        // expressão - retorna 4
    };                  // y = 4

    x + y               // expressão - retornada pela função
}
06

Estruturas de Dados

Arrays (tamanho fixo)

rust
// Array com tipo inferido
let numeros = [1, 2, 3, 4, 5];

// Array com tipo explícito
let nums: [i32; 5] = [1, 2, 3, 4, 5];

// Array preenchido com mesmo valor
let zeros = [0; 10];  // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

// Acessando elementos
let primeiro = numeros[0];
let segundo = numeros[1];

// Iterando
for n in numeros.iter() {
    println!("{}", n);
}

Vectors (tamanho dinâmico)

rust
// Criando vectors
let mut lista = vec![10, 20, 30];
let mut vazio: Vec<i32> = Vec::new();

// Adicionando elementos
lista.push(40);
lista.push(50);

// Removendo último
let ultimo = lista.pop();  // Some(50)

// Acessando (pode causar panic!)
let segundo = lista[1];

// Acesso seguro (retorna Option)
match lista.get(10) {
    Some(valor) => println!("Valor: {}", valor),
    None => println!("Índice inválido"),
}

// Tamanho
println!("Tamanho: {}", lista.len());

Tuplas

rust
// Tupla com tipos diferentes
let pessoa = ("João", 25, true);

// Acessando por índice
let nome = pessoa.0;
let idade = pessoa.1;

// Destructuring
let (nome, idade, ativo) = pessoa;
println!("{} tem {} anos", nome, idade);

HashMap

rust
use std::collections::HashMap;

let mut agenda = HashMap::new();

// Inserindo
agenda.insert("Ana", "1234-5678");
agenda.insert("Carlos", "9876-5432");

// Acessando
if let Some(tel) = agenda.get("Ana") {
    println!("Telefone: {}", tel);
}

// Iterando
for (nome, telefone) in &agenda {
    println!("{}: {}", nome, telefone);
}
07

Ownership e Borrowing

Ownership é o conceito mais importante do Rust! É o que permite segurança de memória sem garbage collector.

As 3 Regras do Ownership

  1. Cada valor tem um dono (owner)
  2. Só pode haver um dono por vez
  3. Quando o dono sai de escopo, o valor é descartado

Move Semantics

rust
let s1 = String::from("hello");
let s2 = s1;  // s1 foi MOVIDO para s2

// println!("{}", s1);  // ERRO! s1 não é mais válido

println!("{}", s2);  // OK!

Clone (cópia profunda)

rust
let s1 = String::from("hello");
let s2 = s1.clone();  // copia os dados

println!("s1: {}, s2: {}", s1, s2);  // ambos válidos!

Borrowing (Referências)

Em vez de mover, podemos emprestar valores:

rust
fn tamanho(s: &String) -> usize {  // recebe referência
    s.len()
}

fn main() {
    let texto = String::from("Rust");
    let tam = tamanho(&texto);  // empresta com &

    // texto ainda é válido!
    println!("{} tem {} caracteres", texto, tam);
}

Referências Mutáveis

rust
fn adicionar_exclamacao(s: &mut String) {
    s.push('!');
}

fn main() {
    let mut texto = String::from("Olá");
    adicionar_exclamacao(&mut texto);
    println!("{}", texto);  // "Olá!"
}

Regra importante: Você pode ter MUITAS referências imutáveis OU UMA referência mutável, mas nunca ambas ao mesmo tempo!

08

Structs e Enums

Structs

rust
// Definindo uma struct
struct Pessoa {
    nome: String,
    idade: u8,
    ativo: bool,
}

// Implementando métodos
impl Pessoa {
    // Construtor (função associada)
    fn new(nome: String, idade: u8) -> Self {
        Pessoa { nome, idade, ativo: true }
    }

    // Método (recebe &self)
    fn apresentar(&self) {
        println!("Olá, sou {} e tenho {} anos", self.nome, self.idade);
    }

    // Método que modifica (recebe &mut self)
    fn fazer_aniversario(&mut self) {
        self.idade += 1;
    }
}

fn main() {
    let mut pessoa = Pessoa::new(String::from("Maria"), 25);
    pessoa.apresentar();
    pessoa.fazer_aniversario();
    println!("Agora tenho {} anos", pessoa.idade);
}

Enums

rust
// Enum simples
enum Status {
    Ativo,
    Inativo,
    Pendente,
}

// Enum com dados associados
enum Mensagem {
    Texto(String),
    Numero(i32),
    Posicao { x: i32, y: i32 },
}

fn main() {
    let status = Status::Ativo;
    let msg = Mensagem::Texto(String::from("Olá!"));

    // Pattern matching com enums
    match msg {
        Mensagem::Texto(s) => println!("Texto: {}", s),
        Mensagem::Numero(n) => println!("Número: {}", n),
        Mensagem::Posicao { x, y } => println!("Posição: ({}, {})", x, y),
    }
}
09

Tratamento de Erros

Option - Para valores opcionais

rust
fn encontrar_par(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            return Some(n);
        }
    }
    None
}

fn main() {
    let nums = vec![1, 3, 5, 6, 7];

    match encontrar_par(&nums) {
        Some(n) => println!("Encontrado: {}", n),
        None => println!("Nenhum par encontrado"),
    }

    // Ou usando if let
    if let Some(n) = encontrar_par(&nums) {
        println!("Par: {}", n);
    }

    // unwrap_or para valor padrão
    let resultado = encontrar_par(&nums).unwrap_or(0);
}

Result - Para operações que podem falhar

rust
fn dividir(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Divisão por zero!"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match dividir(10.0, 2.0) {
        Ok(resultado) => println!("Resultado: {}", resultado),
        Err(erro) => println!("Erro: {}", erro),
    }

    // Operador ? para propagar erros
    fn calcular() -> Result<f64, String> {
        let x = dividir(10.0, 2.0)?;  // retorna erro se falhar
        let y = dividir(x, 2.0)?;
        Ok(y)
    }
}
10

Traits e Generics

Traits (interfaces)

rust
// Definindo um trait
trait Resumo {
    fn resumir(&self) -> String;

    // Método com implementação padrão
    fn preview(&self) -> String {
        format!("Leia mais: {}", self.resumir())
    }
}

struct Artigo {
    titulo: String,
    conteudo: String,
}

// Implementando o trait
impl Resumo for Artigo {
    fn resumir(&self) -> String {
        format!("{}: {}", self.titulo, &self.conteudo[..50])
    }
}

Generics

rust
// Função genérica
fn maior<T: PartialOrd>(a: T, b: T) -> T {
    if a > b { a } else { b }
}

// Struct genérica
struct Ponto<T> {
    x: T,
    y: T,
}

impl<T> Ponto<T> {
    fn new(x: T, y: T) -> Self {
        Ponto { x, y }
    }
}

fn main() {
    let p1 = Ponto::new(5, 10);
    let p2 = Ponto::new(1.0, 2.5);

    println!("Maior: {}", maior(5, 10));
}
11

Arquivos e I/O

rust
use std::fs::File;
use std::io::{Read, Write};

fn main() -> std::io::Result<()> {
    // Escrevendo
    let mut arquivo = File::create("dados.txt")?;
    arquivo.write_all(b"Olá, Rust!")?;

    // Lendo
    let mut conteudo = String::new();
    let mut arquivo = File::open("dados.txt")?;
    arquivo.read_to_string(&mut conteudo)?;
    println!("{}", conteudo);

    Ok(())
}
12

Concorrência

rust
use std::thread;
use std::sync::mpsc;

fn main() {
    // Criando thread
    let handle = thread::spawn(|| {
        println!("Thread executando!");
    });

    handle.join().unwrap();

    // Comunicação entre threads (channels)
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        tx.send("Mensagem da thread").unwrap();
    });

    let msg = rx.recv().unwrap();
    println!("Recebido: {}", msg);
}
13

Testes

rust
fn soma(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_soma_positivos() {
        assert_eq!(soma(2, 2), 4);
    }

    #[test]
    fn test_soma_negativos() {
        assert_eq!(soma(-1, -1), -2);
    }

    #[test]
    #[should_panic]
    fn test_que_deve_falhar() {
        panic!("Este teste deve falhar!");
    }
}

// Rodar testes: cargo test
14

Ecossistema Rust

Crates populares

  • serde - Serialização (JSON, YAML, etc.)
  • tokio - Runtime assíncrono
  • actix-web / axum - Frameworks web
  • clap - Parser de argumentos CLI
  • reqwest - Cliente HTTP
  • diesel / sqlx - ORM e database

Adicionando dependências

bash
cargo add serde --features derive
cargo add tokio --features full
15

Projetos Práticos

1

To-Do List CLI

Lista de tarefas no terminal com adição, remoção e listagem.

2

Conversor de Moedas

Converte valores entre moedas usando taxas de câmbio.

3

Jogo de Adivinhação

Adivinhe o número secreto com dicas de "maior" ou "menor".

4

API REST

CRUD de tarefas com Actix-web ou Axum.

5

CRUD com Persistência

Projeto final com banco de dados SQLite.

16

Carreira

Pratique sempre

Pequenos projetos valem mais que só leitura.

Participe de comunidades

Discord, Telegram, Reddit r/rust.

Contribua com open source

Comece com issues marcadas "good first issue".

Monte um portfólio

Publique projetos no GitHub com README bem escrito.

17

FAQ

Por que o compilador reclama do ownership?

Você está tentando usar um valor que foi movido para outra variável. Use .clone() ou referências &.

Qual a diferença entre String e &str?

String é dinâmica e vive no heap. &str é uma referência a dados de string (pode ser literal ou parte de String).

Como instalar dependências?

Use cargo add nome_da_crate ou adicione manualmente no Cargo.toml.

Como rodar testes?

cargo test executa todos os testes. Use cargo test nome para filtrar.

18

Referências