Rust: Funções e escopo em Rust

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

As funções são cidadãos de primeira classe no Rust — seus tipos de parâmetros e de retorno funcionam como a primeira linha de defesa do compilador.

As funções são os blocos básicos de construção do código. A sintaxe das funções no Rust é semelhante à das linguagens da família C, mas apresenta um estilo único no que diz respeito à correspondência de padrões de argumentos e aos valores de retorno das expressões.


1. O que você vai aprender


2. A história de uma cafeteria

(1) O desafio: não existe um processo padronizado para preparar café

Lisa abriu uma cafeteria, e os negócios vêm indo cada vez melhor, mas também surgiram alguns problemas:

“Seria ótimo se preparar café fosse tão consistente quanto chamar uma função: a mesma entrada sempre produz o mesmo resultado.”

(2) Abordagem para funções em Rust

RUST
// How to Make a Latte"Function":Input Parameters,Brew Coffee
fn make_latte(milk_ml: i32, coffee_ml: i32) -> String {
    let result = format!("Latte Complete:{}ml Milk + {}ml Coffee,Fragrant and Delicious!", milk_ml, coffee_ml);
    result  // No semicolons——Expression Return Value
}

fn main() {
    let my_coffee = make_latte(200, 30);
    println!("{}", my_coffee);

    let another = make_latte(150, 45);
    println!("{}", another);
}

As funções são como receitas de café: os parâmetros são os ingredientes (quantidade de leite, quantidade de café) e o valor de retorno é o produto final. A mesma entrada garante a mesma saída — esse é o determinismo das funções.


3. Definições de funções

(1) Sintaxe básica

RUST
fn function_name(param1: Type1, param2: Type2) -> ReturnType {
    // Function Body
    return_value  // Expression returns(No semicolons)
}
100%
graph TB
    A[fn Function Name] --> B[(List of Parameters)]
    A --> C[-> Return Type]
    A --> D[{ Function Body }]
    B --> E[Each parameter: name: Type]
    D --> F[Final expression = Return Value]
    D --> G[return Keywords = Return Early]

(2) Várias formas de parâmetros e valores de retorno

Forma Sintaxe Exemplo
Sem parâmetros, sem valor de retorno fn foo() fn greet() { println!("hi"); }
Aceita parâmetros, não retorna nada fn foo(x: i32) fn show(n: i32) { println!("{}", n); }
Com parâmetros e um valor de retorno fn foo(x: i32) -> i32 fn double(x: i32) -> i32 { x * 2 }
Vários valores de retorno (tupla) fn foo() -> (i32, bool) fn stats() -> (i32, bool) { (42, true) }
Voltar return value; if x < 0 { return 0; }

Regra básica: A última expressão no corpo da função é o valor de retorno (sem ponto-e-vírgula). Use a palavra-chave return para um retorno antecipado.

(3) Métodos de passagem de parâmetros

Método de passagem Sintaxe Mudança de propriedade Cenários aplicáveis
Passagem por valor fn foo(s: String) A propriedade é transferida para a função A função precisa utilizar o valor
Passagem por referência imutável fn foo(s: &String) Empréstimo, a propriedade permanece inalterada Somente leitura
Passagem por referência fn foo(s: &mut String) Empréstimo, a propriedade permanece inalterada Os dados precisam ser modificados

(4) Comparação dos valores de retorno

Método Sintaxe Mudança de propriedade Exemplo
Retornos de expressão Sem ponto-e-vírgula no final da linha Transferência de propriedade fn f() -> String { s }
Voltar return value; Transferir propriedade if err { return None; }
Retornos com tuplas múltiplas -> (T1, T2) Transferências múltiplas de propriedade fn f() -> (i32, bool)
Parâmetro de saída &mut T Sem transferência de propriedade fn fill(buf: &mut Vec<i32>)

4. Âmbito e sombreamento

(1) Âmbito do bloco

O escopo no Rust é definido por {}. Cada bloco possui seu próprio escopo:

RUST
fn main() {
    let x = 10;          // Outer Scope

    {
        let y = 20;      // Inner Scope,y Available only here
        println!("Inside: x={}, y={}", x, y);  // ✅ Can access the outer layer x
    }

    // println!("{}", y);  // ❌ Compilation Error:y Not in this scope

    println!("External: x={}", x);  // ✅
}

(2) Ocultação de variáveis

Variáveis externas podem ser sobrescritas por variáveis internas com o mesmo nome, e o código interno só pode acessar as variáveis internas:

RUST
fn main() {
    let x = 1;            // Outer layer x = 1

    {
        let x = 2;        // Inner layer x Covered the outer layer x
        println!("Inside x = {}", x);  // Output: Inside x = 2
    }

    println!("External x = {}", x);       // Output: External x = 1
}

(3) Âmbito dos parâmetros de função

Os parâmetros da função são visíveis em todo o corpo da função:

RUST
fn foo(x: i32) {
    // x Available throughout the entire function body
    let y = x + 1;
    println!("x={}, y={}", x, y);
}  // End of function, x and y are both destroyed

5. Exemplos de funções

▶ Exemplo 1: Várias assinaturas de função (Dificuldade ⭐)

RUST
// ============================================
// Functions with Different Combinations of Parameters and Return Types
// ============================================

// No parameters, no return value
fn print_separator() {
    println!("-------------------");
}

// Single-parameter functions with return values
fn square(x: i32) -> i32 {
    x * x  // No semicolons,Expression returns
}

// Multi-parameter
fn area(width: u32, height: u32) -> u32 {
    width * height
}

// Return multiple values(Tuple)
fn min_max(a: i32, b: i32) -> (i32, i32) {
    if a < b {
        (a, b)  // Tuples as Return Values
    } else {
        (b, a)
    }
}

fn main() {
    print_separator();
    println!("5 the square of: {}", square(5));
    println!("Area 10x20: {}", area(10, 20));

    let (min, max) = min_max(100, 50);
    println!("min={}, max={}", min, max);
}

Resultado:

TEXT 📖 Somente leitura
-------------------
5 the square of: 25
Area 10x20: 200
min=50, max=100

min_max Retorna uma tupla; quando chamada, ela é desestruturada e atribuída usando let (min, max). Essa é a maneira padrão de “retornar vários valores” em Rust.


▶ Exemplo 2: Retorno antecipado e modo de guarda (Dificuldade ⭐⭐)

RUST
// ============================================
// Return Early:Inspection Criteria,If you're not satisfied, leave.
// ============================================

fn safe_divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        println!("Error:The divisor cannot be 0");
        return f64::NAN;  // Return Early NaN(Not a Number)
    }
    a / b  // Under normal circumstances,Expression returns
}

fn get_grade(score: i32) -> &'static str {
    if score < 0 || score > 100 {
        return "Invalid Fractions";
    }
    if score >= 90 { return "A"; }
    if score >= 80 { return "B"; }
    if score >= 70 { return "C"; }
    if score >= 60 { return "D"; }
    "F"
}

fn main() {
    println!("10 / 3 = {}", safe_divide(10.0, 3.0));
    println!("10 / 0 = {}", safe_divide(10.0, 0.0));
    println!("Fractions 85: {}", get_grade(85));
    println!("Fractions -5: {}", get_grade(-5));
}

Resultado:

TEXT 📖 Somente leitura
10 / 3 = 3.3333333333333335
Error:The divisor cannot be 0
10 / 0 = NaN
Fractions 85: B
Fractions -5: Invalid Fractions

O retorno antecipado (return) é comumente usado no padrão “verificar primeiro, depois executar”. Ao lidar com condições de erro e casos extremos logo no início, a lógica subsequente prossegue sem problemas dentro de uma “zona segura”.


▶ Exemplo 3: Expressões em bloco e teste de escopo (Dificuldade ⭐⭐)

RUST
// ============================================
// Scope and Return Values of Block Expressions
// ============================================

fn main() {
    let outer = "Outer-scope variables".to_string();

    let inner_result = {
        let inner = "Nested variables".to_string();
        println!("Accessible internally: {}", outer);  // ✅ The inner layer can access the outer layer

        // Shading Variables with the Same Name
        let outer = 42;
        println!("After masking outer: {}", outer);  // 42,Not a string

        inner.len()  // Block expression return values:inner length
    };  // inner Destroyed here

    println!("The value returned by the block: {}", inner_result);
    println!("External outer: {}", outer);  // ✅ Outer layer outer Still

    // println!("{}", inner);  // ❌ Compilation Error:inner Not in this scope
}

Resultado:

TEXT 📖 Somente leitura
Accessible internally: Outer-scope variables
After masking outer: 42
The value returned by the block: 8
External outer: Outer-scope variables

Regras de escopo dos blocos: os blocos internos podem acessar variáveis externas, mas os blocos externos não podem acessar variáveis internas. O sombreamento apenas “oculta” temporariamente as variáveis externas; assim que você sai do bloco interno, as variáveis externas voltam ao normal.


▶ Exemplo 4: Ponteiros de função e funções de ordem superior (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Passing Functions as Arguments——An Introduction to Higher-Order Functions
// ============================================

type MathOp = fn(i32, i32) -> i32;

fn add(a: i32, b: i32) -> i32 { a + b }
fn sub(a: i32, b: i32) -> i32 { a - b }
fn mul(a: i32, b: i32) -> i32 { a * b }

fn calculate(op: MathOp, a: i32, b: i32) -> i32 {
    op(a, b)
}

fn apply_twice(f: fn(i32) -> i32, x: i32) -> i32 {
    f(f(x))
}

fn main() {
    let result1 = calculate(add, 10, 5);
    let result2 = calculate(sub, 10, 5);
    let result3 = calculate(mul, 10, 5);
    println!("10 + 5 = {}", result1);
    println!("10 - 5 = {}", result2);
    println!("10 * 5 = {}", result3);

    let double = |x: i32| x * 2;
    let result4 = apply_twice(double, 3);
    println!("double(double(3)) = {}", result4);

    let increment = |x: i32| x + 1;
    let result5 = apply_twice(increment, 5);
    println!("increment(increment(5)) = {}", result5);

    let alice_points = 80;
    let bonus = |base: i32| -> i32 { base + 10 };
    println!("Alice After the bonus points are added: {}", bonus(alice_points));
}

Resultado:

TEXT 📖 Somente leitura
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
double(double(3)) = 12
increment(increment(5)) = 7
Alice After the bonus points are added: 90

Os ponteiros de função (do tipo fn) e os closures (do tipo |x| x * 2) podem ser passados como argumentos para funções de ordem superior. Os ponteiros de função apontam para endereços de código determinados em tempo de compilação, enquanto os closures podem capturar variáveis de ambiente.


▶ Exemplo 5: Exercício abrangente — Processamento das notas dos alunos (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:Function,Combining Scope and Shading
// ============================================

fn average(scores: &[i32]) -> f64 {
    if scores.is_empty() {
        return 0.0;
    }
    let sum: i32 = scores.iter().sum();
    sum as f64 / scores.len() as f64
}

fn classify(score: i32) -> &'static str {
    match score {
        90..=100 => "A",
        80..=89  => "B",
        70..=79  => "C",
        60..=69  => "D",
        _        => "F",
    }
}

fn report(names: &[&str], scores: &[i32]) {
    let avg = average(scores);
    println!("Class Average Score: {:.1}", avg);

    for (i, name) in names.iter().enumerate() {
        let score = scores[i];
        let grade = classify(score);
        let status = if score >= 60 { "Through" } else { "Failed" };
        println!("{}: {} pts, Level {}, {}", name, score, grade, status);
    }

    let passing = scores.iter().filter(|&&s| s >= 60).count();
    println!("Number of people who passed: {}/{}", passing, scores.len());
}

fn main() {
    let names = ["Alice", "Bob", "Charlie", "David"];
    let scores = [95, 72, 58, 88];

    println!("=== Report Card ===");
    report(&names, &scores);

    println!("\n--- Scope Demonstration ---");
    let result = {
        let scores = [100, 90, 80];
        let avg = average(&scores);
        format!("Average of the top three: {:.1}", avg)
    };
    println!("{}", result);

    let avg = "Outer layer avg";
    println!("Outer-scope variables: {}", avg);
}

Resultado:

TEXT 📖 Somente leitura
=== Report Card ===
Class Average Score: 78.2
Alice: 95 pts, Level A, Through
Bob: 72 pts, Level C, Through
Charlie: 58 pts, Level F, Failed
David: 88 pts, Level B, Through
Number of people who passed: 3/4

--- Scope Demonstration ---
Average of the top three: 90.0
Outer-scope variables: Outer layer avg

Este exemplo combina o uso de definições de funções, parâmetros de fatia, classificação match, métodos de iterador e ocultação de variáveis no escopo de bloco. Usar a fatia &[T] como parâmetro de função oferece a maior flexibilidade, e ocultar uma variável com o mesmo nome dentro do escopo de um bloco não afeta o escopo externo.


❓ Perguntas Frequentes

P: A expressão na última linha não inclui um ponto-e-vírgula como valor de retorno. O que devo fazer se costumo esquecer de adicionar o ponto-e-vírgula? R: Se você esquecer o ponto-e-vírgula, o compilador exibirá um erro, solicitando que você especifique se se trata de uma instrução ou de uma expressão. O compilador do Rust é muito inteligente nesse aspecto; ele perguntará: “Você esqueceu o ponto-e-vírgula?” ou “Deseja retornar um valor?”. Depois que você se acostumar, vai achar esse design bastante elegante.

P: Qual é a diferença entre return e um retorno por expressão? R: return é usado para retornos antecipados, enquanto o retorno por expressão é usado no final de uma função. Você também pode usar return no final de uma função, mas a convenção é “usar uma expressão no final e return para retornos antecipados”.

P: Por que os tipos dos parâmetros de função devem ser especificados? R: Porque o Rust não infere os tipos dos parâmetros de função. A assinatura de uma função é um contrato de API; especificar explicitamente os tipos deixa claro para quem a chama o que precisa ser passado. Essa é uma escolha de design do Rust: clareza em vez de concisão.

P: O shadowing é uma prática inadequada? R: O shadowing moderado é uma boa prática. Por exemplo, converter String em &str e, em seguida, continuar a processá-la usando uma variável com o mesmo nome torna o código mais conciso. No entanto, você deve evitar o shadowing excessivo que leve à confusão — é melhor limitar a hierarquia de shadowing a no máximo dois níveis.

P: É possível aninhar funções? R: Sim, o Rust permite definir funções dentro de outras funções. As funções internas só são visíveis dentro do escopo da função em que estão definidas e não podem ser chamadas de fora desse escopo. Isso é útil quando você precisa de funções auxiliares, mas não quer expô-las ao exterior.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Escreva uma função fn is_even(n: i32) -> bool que determine se um número inteiro é par e, em seguida, escreva main para chamá-la e exibir a paridade de cada número de 1 a 10.
  2. Dificuldade ⭐⭐: Escreva uma função fn max_of_three(a: i32, b: i32, c: i32) -> i32 que retorne o maior valor entre três números. (Não é permitido usar a função .max() da biblioteca padrão; escreva sua própria lógica de comparação.)
  3. Dificuldade ⭐⭐⭐: Escreva uma função fn calculator(op: char, a: f64, b: f64) -> f64 que execute a operação correspondente com base nos parâmetros op ('+' '-' '*' '/') e retorne f64::NAN quando o divisor for 0. Teste essas quatro operações em main.
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%