Pular para o conteúdo

Funções em Rust

Uma função é um bloco de código com nome que você pode reaproveitar. Em vez de escrever os mesmos passos de novo e de novo, você os coloca uma vez em uma função e a chama sempre que precisar. Você já vem usando uma: a main.

Você define uma função com fn e a chama escrevendo o nome dela seguido de ():

fn greet() {
println!("Olá de dentro de uma função!");
}
fn main() {
greet(); // chama a função
greet(); // chama de novo
}

Saída:

Olá de dentro de uma função!
Olá de dentro de uma função!
▶ Experimente você mesmo

As funções ficam úteis de verdade quando você passa dados para elas trabalharem. Essas entradas se chamam parâmetros. Em Rust, você precisa escrever o tipo de cada parâmetro:

fn greet(name: &str) {
println!("Olá, {name}!");
}
fn main() {
greet("Alice");
greet("Bob");
}

Saída:

Olá, Alice!
Olá, Bob!
▶ Experimente você mesmo

Você pode ter quantos parâmetros precisar, separados por vírgulas:

fn describe(name: &str, age: u32) {
println!("{name} tem {age} anos");
}
fn main() {
describe("Ferris", 10);
}

Saída:

Ferris tem 10 anos
▶ Experimente você mesmo

Os valores que você passa são ligados aos parâmetros pela posição: o primeiro valor vai para name, o segundo para age. Passá-los na ordem errada, como em describe(10, "Ferris"), é um erro de compilação, porque os tipos não combinam.

Uma função pode devolver um valor para quem a chamou. Você declara o tipo de retorno depois de uma seta ->:

fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let sum = add(3, 4);
println!("3 + 4 = {sum}");
}

Saída:

3 + 4 = 7
▶ Experimente você mesmo

Repare que o a + b no exemplo acima não tem ponto e vírgula. Essa é uma ideia central do Rust:

  • Uma expressão sem ponto e vírgula é o valor que será retornado.
  • Colocar um ponto e vírgula a transforma em uma instrução que não retorna nada.

Então isto quebra:

fn add(a: i32, b: i32) -> i32 {
a + b; // ❌ o ponto e vírgula quer dizer "não retorne nada", mas prometemos um i32
}

O compilador vai reclamar que a função deveria retornar um i32, mas não retorna. É só tirar o ponto e vírgula da última linha.

fn square(n: i32) -> i32 {
n * n
}
fn main() {
let result = square(5);
println!("5 ao quadrado é {result}");
}

Saída:

5 ao quadrado é 25
▶ Experimente você mesmo

Escreva uma função to_fahrenheit que recebe uma temperatura em Celsius como f64 e a devolve em Fahrenheit. A fórmula é c * 9.0 / 5.0 + 32.0. Chame a função para 0, 100 e 37 graus. Dica: a assinatura é fn to_fahrenheit(c: f64) -> f64, e o corpo é uma única expressão sem ponto e vírgula.

Ver solução
fn to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn main() {
println!("0°C são {}°F", to_fahrenheit(0.0));
println!("100°C são {}°F", to_fahrenheit(100.0));
println!("37°C são {}°F", to_fahrenheit(37.0));
}

Saída:

0°C são 32°F
100°C são 212°F
37°C são 98.6°F
▶ Experimente você mesmo

Repare que passamos 0.0, e não 0. O parâmetro é um f64, e o Rust não transforma um inteiro em número de ponto flutuante por conta própria. Quando um f64 não tem parte fracionária, o {} o imprime sem o .0, e é por isso que aparece 32 e não 32.0. Note também que o Rust usa ponto como separador decimal: 98.6.

Escreva uma função largest(a: i32, b: i32, c: i32) -> i32 que devolve o maior dos três números. Teste com (3, 9, 4) e (-2, -8, -5). Dica: o if é uma expressão em Rust (veja If…Else), então uma cadeia de if / else if / else pode ser a última linha da função.

Ver solução
fn largest(a: i32, b: i32, c: i32) -> i32 {
if a >= b && a >= c {
a
} else if b >= c {
b
} else {
c
}
}
fn main() {
println!("{}", largest(3, 9, 4));
println!("{}", largest(-2, -8, -5));
}

Saída:

9
-2
▶ Experimente você mesmo

Nenhum dos ramos termina com ponto e vírgula. Cada um é uma expressão, a cadeia de if inteira devolve um deles, e esse vira o valor de retorno. Usar >= em vez de > garante que, em caso de empate, a função ainda devolva um valor.

  • Defina funções com fn e chame-as com nome().
  • Parâmetros são entradas, e cada um precisa de um tipo: fn greet(name: &str).
  • Declare o tipo de retorno com -> Tipo.
  • A última expressão sem ponto e vírgula é o valor de retorno.
  • return valor; também funciona, mas costuma ser usado só para retornar mais cedo.

Teste o que aprendeu

1. Em Rust, é preciso escrever o tipo de cada parâmetro de uma função?

2. Como se declara o tipo de retorno de uma função?

3. Por que a + b (sem ponto e vírgula) no fim de uma função vira o valor de retorno?

4. Faz diferença definir uma função acima ou abaixo de onde ela é chamada (por exemplo, acima ou abaixo da main)?

Pontuação: 0 / 4