Rust: Controle de fluxo em Rust

Última atualização: 2026-08-26

O match do Rust não é um switch — ele é mais poderoso porque o compilador verifica se você cobriu todos os casos.

O fluxo de controle é a espinha dorsal de um programa. Além do tradicional if/else, o Rust possui dois pontos fortes principais: match correspondência de padrões e if let sintaxe simplificada.


1. O que você vai aprender


2. A história do árbitro do “pedra, papel e tesoura”

(1) O desafio: escrever uma lógica de decisão para o jogo “pedra, papel e tesoura” usando instruções if-else

Emma é uma desenvolvedora de jogos que quer adicionar um recurso de Pedra, Papel e Tesoura ao seu jogo:

TEXT 📖 Somente leitura
Player's turn: Rock (1), Scissors (2), Paper (3)
Computer: Pick one at random
Needs to be determined: Win, Lose, or Tie

Código que utiliza if/else para determinação:

TEXT 📖 Somente leitura
if Player == 1 && Computer == 1 → Tie
else if Player == 1 && Computer == 2 → Win
else if Player == 1 && Computer == 3 → Lose
... Still needs 6 branches!

Escrever um monte de if/else faz com que seja fácil deixar passar certas combinações, além de dificultar a leitura.

(2) Uma solução elegante para a instrução match do Rust

RUST
fn main() {
    let player = 1;    // Rock
    let computer = 2;  // Scissors

    let result = match (player, computer) {
        (1, 1) | (2, 2) | (3, 3) => "Tie",
        (1, 2) | (2, 3) | (3, 1) => "You Win!",
        (1, 3) | (2, 1) | (3, 2) => "You Lose!",
        _ => "Invalid Punch",
    };

    println!("Results: {}", result);
}

match É como um árbitro aqui: ele mapeia as combinações de socos do jogador e do computador uma a uma, sem omissões nem ambiguidades. O compilador verifica se você escreveu todas as 9 combinações.


3. Ramificação condicional if/else

(1) Uso básico

RUST
fn main() {
    let number = 7;

    if number < 5 {
        println!("A number less than 5");
    } else if number == 5 {
        println!("The number equals 5");
    } else {
        println!("The number is greater than 5");
    }
}

(2) Se if for uma expressão, ela pode retornar um valor

RUST
let condition = true;
let value = if condition { 5 } else { 6 };
// Note:The two branches must be of the same type.
println!("value = {}", value);  // value = 5

Importante: Os dois ramos de if devem ser do mesmo tipo. if true { 5 } else { "six" } resultará em um erro de compilação.


4. Correspondência de padrões

(1) Sintaxe básica

100%
graph TB
    A[match Expressions] --> B[Each branch = A Pattern]
    B --> C[Pattern match successful → Run this branch]
    B --> D[Pattern Mismatch → Try the next branch]
    A --> E[Finally, there must be _ Wildcard Fallback]
RUST
let number = 3;
match number {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("other"),  // Wildcard——Match all remaining cases
}

(2) Regras principais para “match”

Regra Descrição
Exaustividade Deve abranger todos os valores possíveis; a omissão de qualquer ramificação resultará em um erro de compilação
Caractere curinga _ Corresponde a todos os casos restantes; colocado no final
Multimodo | Um ramo corresponde a vários padrões: 1 | 2 =>
Correspondência de intervalo 1..=5 Corresponde ao intervalo fechado de 1 a 5
Guarda Condições adicionais: x if x > 5 =>

(3) Comparando if e match

Características if/else match
Método de correspondência Condição booleana Correspondência de padrões
Verificação exaustiva Nenhuma (caso contrário, pode ser omitida) Obrigatória (deve abranger todos os valores)
Recursos de desconstrução Nenhum Suporta a desconstrução de enumerações, tuplas e estruturas
Correspondência com vários valores Requer concatenação `
Casos de uso Verificações de intervalo, lógica booleana Processamento de enumeração, classificação de valores, desconstrução

5. if let – Açúcar sintático

Quando você está interessado apenas em um padrão específico e não em outros valores, if let é mais conciso do que match:

RUST
let optional = Some(5);

// match Writing Style (verbose)
match optional {
    Some(value) => println!("The value is: {}", value),
    _ => (),  // You must write this empty branch.
}

// if let Writing Style (Concise)
if let Some(value) = optional {
    println!("The value is: {}", value);
    // No need to write _ => ()
}

(4) Referência rápida para palavras-chave de fluxo de controle

Palavra-chave/Sintaxe Finalidade Exemplo
if / else if / else Ramificação condicional if x > 0 { ... } else { ... }
match Correspondência de padrões match x { 1 => ..., _ => ... }
if let Sintaxe simplificada para correspondência em modo único if let Some(v) = x { ... }
while Laços condicionais while x > 0 { ... }
while let Ciclo de correspondência de padrões while let Some(v) = iter.next() { ... }
loop Loop Infinito loop { ... break; }
Percorrimento do iterador
break Sair do ciclo break; ou break value;
continue Pular esta iteração continue;

6. Exemplo completo

▶ Exemplo 1: Classificação de notas com if/else (Dificuldade ⭐)

RUST
// ============================================
// Use if expression to grade scores and return a string
// ============================================

fn main() {
    let score = 88;

    let grade = if score >= 90 {
        "Excellent (A)"
    } else if score >= 80 {
        "Good (B)"
    } else if score >= 70 {
        "Intermediate (C)"
    } else if score >= 60 {
        "Passing Grade (D)"
    } else {
        "Fail (F)"
    };

    println!("Fractions: {}, Level: {}", score, grade);
}

Resultado:

TEXT 📖 Somente leitura
Fractions: 88, Level: Good (B)

if Cada ramificação da expressão deve retornar o mesmo tipo. Aqui, todas as ramificações retornam &str, então o compilador está satisfeito.


▶ Exemplo 2: Usando match para lidar com códigos de status HTTP (Dificuldade: ⭐⭐)

RUST
// ============================================
// Use match to gracefully handle HTTP Status Codes
// ============================================

fn handle_status_code(code: u16) -> &'static str {
    match code {
        200 => "OK: Request Successful",
        201 => "Created: The resource has been created",
        204 => "No Content: No results found",
        301 | 302 => "Redirect: The resource has been moved",
        400 => "Bad Request: Invalid request format",
        401 => "Unauthorized: Unauthorized",
        403 => "Forbidden: Access Denied",
        404 => "Not Found: Resource does not exist",
        500 => "Internal Server Error: Internal Server Error",
        502 | 503 => "Server Error: The server is temporarily unavailable",
        _ => "Unknown: Unknown status code",
    }
}

fn main() {
    let codes = [200, 404, 418, 500];
    for &code in codes.iter() {
        println!("Status Code {}: {}", code, handle_status_code(code));
    }
}

Resultado:

TEXT 📖 Somente leitura
Status Code 200: OK: Request Successful
Status Code 404: Not Found: Resource does not exist
Status Code 418: Unknown: Unknown status code
Status Code 500: Internal Server Error: Internal Server Error

| O operador permite que uma ramificação corresponda a vários padrões, evitando a duplicação de código. _ Os caracteres curinga garantem que todos os códigos de status sejam abrangidos.


▶ Exemplo 3: match guards e if let (Dificuldade ⭐⭐)

RUST
// ============================================
// match Guard + if let Simplified Pattern Matching
// ============================================

fn main() {
    let number = Some(42);

    // match Guard: Add additional conditions to the pattern
    match number {
        Some(x) if x > 100 => println!("Very Large Numbers: {}", x),
        Some(x) if x > 50  => println!("Big Numbers: {}", x),
        Some(x)             => println!("Small numbers: {}", x),
        None                => println!("No numbers"),
    }

    // if let Syntactic sugar -- Only care about the Some case
    let value = Some("Rust");
    if let Some(lang) = value {
        println!("Language is: {}", lang);
    }

    // if let with else
    let empty: Option<i32> = None;
    if let Some(x) = empty {
        println!("Not null: {}", x);
    } else {
        println!("No value");
    }
}

Resultado:

TEXT 📖 Somente leitura
Small numbers: 42
Language is: Rust
No value

Use a palavra-chave if em uma instrução match para adicionar uma condição adicional após o padrão — esse ramo só será executado se o padrão corresponder e a condição for verdadeira. if let é adequada para situações em que você está “preocupado apenas com um padrão”.


▶ Exemplo 4: Usando match para desconstruir enums e tuplas (Dificuldade: ⭐⭐⭐)

RUST
// ============================================
// match Deconstruction: Enumeration + Tuple + Guard
// ============================================

enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Triangle(f64, f64, f64),
}

fn describe_shape(shape: &Shape) -> String {
    match shape {
        Shape::Circle(radius) if *radius > 10.0 => {
            format!("Big Circle (Radius {:.1})", radius)
        }
        Shape::Circle(radius) => {
            format!("Small Circle (Radius {:.1})", radius)
        }
        Shape::Rectangle(w, h) if w == h => {
            format!("Square (Side length {:.1})", w)
        }
        Shape::Rectangle(w, h) => {
            format!("Rectangle (Width {:.1}, Height {:.1})", w, h)
        }
        Shape::Triangle(a, b, c) => {
            format!("Triangle (Sides {:.1}, {:.1}, {:.1})", a, b, c)
        }
    }
}

fn main() {
    let shapes = [
        Shape::Circle(5.0),
        Shape::Circle(15.0),
        Shape::Rectangle(4.0, 4.0),
        Shape::Rectangle(3.0, 5.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    println!("=== Graphical Description ===");
    for shape in &shapes {
        println!("{}", describe_shape(shape));
    }

    println!("\n=== Coordinate Classification ===");
    let points: [(i32, i32); 4] = [(0, 0), (3, 0), (0, -2), (5, 7)];
    for point in points {
        let desc = match point {
            (0, 0) => "Origin".to_string(),
            (x, 0) => format!("On x-axis (x={})", x),
            (0, y) => format!("On y-axis (y={})", y),
            (x, y) if x > 0 && y > 0 => format!("First Quadrant ({}, {})", x, y),
            (x, y) => format!("Other Quadrants ({}, {})", x, y),
        };
        println!("Point {:?}: {}", point, desc);
    }
}

Resultado:

TEXT 📖 Somente leitura
=== Graphical Description ===
Small Circle (Radius 5.0)
Big Circle (Radius 15.0)
Square (Side length 4.0)
Rectangle (Width 3.0, Height 5.0)
Triangle (Sides 3.0, 4.0, 5.0)

=== Coordinate Classification ===
Point (0, 0): Origin
Point (3, 0): On x-axis (x=3)
Point (0, -2): On y-axis (y=-2)
Point (5, 7): First Quadrant (5, 7)

Os recursos de desestruturação do match permitem que você, simultaneamente, identifique variantes de enumeração, extraia valores internos e aplique condições de verificação para implementar uma lógica de classificação precisa. A verificação exaustiva do compilador garante que você não deixe passar nenhum caso.


▶ Exemplo 5: Aplicação prática de if let e while let (Dificuldade: ⭐⭐)

RUST
// ============================================
// if let and while let: Simplify Option/Result Processing
// ============================================

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    println!("=== if let Processing Option ===");
    let results = [divide(10.0, 2.0), divide(8.0, 0.0), divide(15.0, 3.0)];

    for result in &results {
        if let Some(value) = result {
            println!("Division Successful: {:.2}", value);
        } else {
            println!("Division Failed: Divisor is zero");
        }
    }

    println!("\n=== if let Chain Processing ===");
    let alice_input: Option<&str> = Some("42");
    let bob_input: Option<&str> = None;

        if let Some(num_str) = alice_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Alice's number: {}", num);
        }
    }

    if let Some(num_str) = bob_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Bob's number: {}", num);
        }
    } else {
        println!("Bob has no data input");
    }

    println!("\n=== while let Simulation Iteration ===");
    let mut stack = vec![3, 2, 1];
    while let Some(top) = stack.pop() {
        println!("Popped: {}", top);
    }
    println!("The stack is empty");
}

Resultado:

TEXT 📖 Somente leitura
=== if let Processing Option ===
Division Successful: 5.00
Division Failed: Divisor is zero
Division Successful: 5.00

=== if let Chain Processing ===
Alice's number: 42
Bob has no data input

=== while let Simulation Iteration ===
Popped: 1
Popped: 2
Popped: 3
The stack is empty

if let é adequado para cenários em que você se preocupa apenas com Some/Ok e pode ignorar outros casos; while let é adequado para cenários em que você percorre os valores até encontrar None (como nas operações de pilha pop()). Ambas são mais concisas do que o match completo.


❓ Perguntas Frequentes

P: Qual é a diferença entre match e switch? R: match é muito mais poderoso do que switch. match verifica se todos os ramos foram cobertos (exaustivo), suporta decomposição de padrões, suporta condições de guarda e não requer break (não permite fall-through).

P: Por que a verificação exaustiva match é tão importante? R: Porque é “o compilador ajudando você a encontrar bugs”. Se você adicionar uma nova variante a uma enumeração, mas se esquecer de atualizar a instrução match, o compilador irá relatar um erro. Isso torna a refatoração de grandes bases de código mais segura.

P: Quando se deve usar if let? R: Quando você estiver interessado em apenas um padrão. Por exemplo, ao lidar com Option, if you're only interested in Some and not None, using if let Some(x) = value is more concise than writing the full match.

P: Qual é a diferença entre o caractere curinga _ e um nome de variável em match? R: O _ ignora completamente o valor e não o vincula, enquanto um nome de variável vincula o valor, mas pode gerar um aviso do compilador por variáveis não utilizadas. Se você precisar corresponder a qualquer valor, mas não precisar usá-lo, use _; se precisar usá-lo, dê um nome a ele.

P: O que devo fazer se os dois ramos de uma expressão if forem de tipos diferentes? R: Ocorrerá um erro de compilação. Ambos os ramos devem ser do mesmo tipo. Uma solução comum é usar () in both branches or have both branches return Option or Result para unificar os tipos.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Escreva uma função fn number_to_word(n: i32) -> &'static str que utilize match para mapear os dígitos de 0 a 5 para as palavras em inglês correspondentes (zero/one/two/three/four/five) e retorne “unknown” para todos os outros dígitos.
  2. Dificuldade ⭐⭐: Defina uma enumeração enum TrafficLight { Red, Yellow, Green } e use match para retornar o “tempo de espera” correspondente a cada cor (vermelho: 30 s, amarelo: 3 s, verde: 45 s).
  3. Dificuldade ⭐⭐⭐ : Escreva uma função fn describe_point(point: (i32, i32)) que utilize match para processar um ponto de coordenadas bidimensionais: se o ponto estiver na origem (0,0), exiba “origem”; se estiver no eixo x (x,0), exiba “no eixo x”; se estiver no eixo y (0,y), exiba “no eixo y”; em todos os outros casos, exiba os valores das coordenadas.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%