Rust: Controle de fluxo em Rust
Última atualização: 2026-08-26
O
matchdo Rust não é umswitch— 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
- Use
if/else if/elsepara ramificação condicional - Use
matchpara a correspondência de padrões - Verificação exaustiva de “correspondências” e caracteres curinga
_ - Use
if letpara simplificar a correspondência em modo único - Adicionar condições adicionais à regra “match guard”
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:
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:
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/elsefaz 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
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
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
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
ifdevem 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
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]
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:
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 ⭐)
// ============================================
// 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:
Fractions: 88, Level: Good (B)
ifCada 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: ⭐⭐)
// ============================================
// 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:
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 ⭐⭐)
// ============================================
// 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:
Small numbers: 42
Language is: Rust
No value
Use a palavra-chave
ifem 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: ⭐⭐⭐)
// ============================================
// 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:
=== 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
matchpermitem 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: ⭐⭐)
// ============================================
// 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:
=== 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 comSome/Oke pode ignorar outros casos;while leté adequado para cenários em que você percorre os valores até encontrarNone(como nas operações de pilhapop()). Ambas são mais concisas do que omatchcompleto.
❓ Perguntas Frequentes
P: Qual é a diferença entre
matcheswitch? R:matché muito mais poderoso do queswitch.matchverifica se todos os ramos foram cobertos (exaustivo), suporta decomposição de padrões, suporta condições de guarda e não requerbreak(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çãomatch, 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 comOption, if you're only interested inSomeand notNone, usingif let Some(x) = valueis more concise than writing the fullmatch.
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
ifforem 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 returnOptionorResultpara unificar os tipos.
📖 Resumo
if/elseé uma expressão (que pode retornar um valor); todos os ramos devem ser do mesmo tipomatchAo realizar a correspondência de padrões, o compilador impõe uma verificação exaustiva- O curinga
_corresponde a todos os valores restantes e é colocado no final da correspondência if leté uma sintaxe simplificada paramatch, adequada para situações em que você está “preocupado apenas com um padrão”- O guard (com a condição
if) torna a correspondência de padrões mais flexível - O operador
|permite que um ramo corresponda a vários padrões
📝 Exercícios
- Dificuldade ⭐: Escreva uma função
fn number_to_word(n: i32) -> &'static strque utilizematchpara 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. - Dificuldade ⭐⭐: Defina uma enumeração
enum TrafficLight { Red, Yellow, Green }e usematchpara retornar o “tempo de espera” correspondente a cada cor (vermelho: 30 s, amarelo: 3 s, verde: 45 s). - Dificuldade ⭐⭐⭐ : Escreva uma função
fn describe_point(point: (i32, i32))que utilizematchpara 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.