Funções em Rust
Esta aula aborda funções em Rust, cobrindo declaração, parâmetros e tipos, retorno e expressões, e a diferença entre statements e expressions. O conteúdo inclui exemplos práticos e explicações detalhadas para alunos intermediários.
Funções são blocos fundamentais de código em Rust, permitindo organizar lógica, reutilizar código e abstrair comportamentos. Nesta aula, exploraremos como declarar funções, passar parâmetros com tipos explícitos, retornar valores e entender a diferença crucial entre statements e expressions, que influencia diretamente a sintaxe de retorno.
Em Rust, funções são definidas com a palavra-chave fn, seguida por um nome, parâmetros opcionais entre parênteses, e um corpo delimitado por chaves. A linguagem é fortemente tipada, então todos os parâmetros e o tipo de retorno devem ser declarados. Vamos mergulhar nos detalhes.
Declaração
A declaração de uma função em Rust começa com fn, seguido pelo nome da função (em snake_case por convenção), parênteses para parâmetros, e chaves para o corpo. Se a função não retorna valor, o tipo de retorno é omitido (equivalente a (), a tupla vazia).
Exemplo de uma função simples que imprime uma mensagem:
fn saudacao() {
println!("Olá, mundo!");
}Note que não há ponto e vírgula após a declaração; o corpo é um bloco de código. Funções podem ser chamadas de qualquer lugar dentro do escopo onde são visíveis.
Parâmetros e tipos
Parâmetros são declarados dentro dos parênteses, com nome seguido de dois pontos e o tipo. Múltiplos parâmetros são separados por vírgulas. Rust exige que todos os parâmetros tenham tipos explícitos, o que ajuda na segurança e clareza.
Exemplo de função que soma dois números:
fn soma(a: i32, b: i32) -> i32 {
a + b
}Os parâmetros a e b são do tipo i32 (inteiro de 32 bits). O tipo de retorno é especificado após uma seta ->. A expressão a + b é o valor retornado (sem ponto e vírgula).
É importante notar que Rust não suporta valores padrão para parâmetros nem sobrecarga de funções (diferentes assinaturas com o mesmo nome). Cada função tem um nome único.
Retorno e expressões
Em Rust, o valor de retorno de uma função é a última expressão do corpo, se não houver ponto e vírgula. Alternativamente, pode-se usar a palavra-chave return para retornar prematuramente. A expressão de retorno deve ser do tipo declarado.
Exemplo com return:
fn maior(a: i32, b: i32) -> i32 {
if a > b {
return a;
}
b
}Aqui, se a > b, retornamos a imediatamente; caso contrário, a última expressão b é retornada. Note que b não tem ponto e vírgula, pois é uma expressão.
Funções também podem retornar múltiplos valores usando tuplas:
fn divisao_com_resto(a: i32, b: i32) -> (i32, i32) {
(a / b, a % b)
}Statements vs expressions
Em Rust, todo código é composto por statements (instruções) e expressions (expressões). Statements são comandos que realizam uma ação e não retornam valor (ex.: declaração de variável com let, chamada de função com ponto e vírgula). Expressions avaliam para um valor e podem ser compostas (ex.: 5 + 3, if blocks, blocos de código).
Um bloco {} é uma expression que retorna o valor da sua última expressão. Por exemplo:
fn main() {
let y = {
let x = 3;
x + 1
};
println!("O valor de y é: {}", y); // imprime 4
}Aqui, o bloco { let x = 3; x + 1 } é uma expression que retorna 4. Note que let x = 3; é um statement (termina com ponto e vírgula), enquanto x + 1 é uma expression (sem ponto e vírgula).
Entender essa diferença é crucial para escrever funções corretas: a última linha do corpo da função deve ser uma expression sem ponto e vírgula para que o valor seja retornado. Se você colocar um ponto e vírgula, a linha se torna um statement e a função retorna () (tupla vazia), a menos que explicitamente use return.
Boas práticas
- Use nomes descritivos em snake_case para funções.
- Prefira retornar valores implicitamente (sem
return) para clareza, a menos que precise de retorno prematuro. - Evite funções muito longas; quebre em funções menores.
- Documente funções públicas com
///para gerar documentação.
Funções são a base para módulos, traits e closures, que veremos em aulas futuras.
Referências
- The Rust Programming Language - Functions
- Rust by Example - Functions
- Rust Reference - Functions
- Rust std - fn keyword
- The Rust Programming Language - Control Flow (expressions)
- Rust by Example - Expressions
Exercícios
Escreva uma função chamada
quadradoque recebe um inteiroi32e retorna o seu quadrado (tambémi32).✓ Resposta:fn quadrado(x: i32) -> i32 { x * x }Crie uma função
eh_parque recebe umi32e retorna um booleano indicando se o número é par.✓ Resposta:fn eh_par(n: i32) -> bool { n % 2 == 0 }Implemente uma função
fatorialque calcula o fatorial de um número inteiro não negativo (u32) usando recursão. Retorneu64para evitar overflow.✓ Resposta:fn fatorial(n: u32) -> u64 { if n == 0 { 1 } else { n as u64 * fatorial(n - 1) } }Escreva uma função
soma_tuplaque recebe uma tupla(i32, i32)e retorna a soma dos dois elementos comoi32.✓ Resposta:fn soma_tupla(t: (i32, i32)) -> i32 { t.0 + t.1 }Crie uma função
maior_de_tresque recebe três inteirosi32e retorna o maior deles. Use a funçãomaxda biblioteca padrão se desejar, ou implemente manualmente.✓ Resposta:fn maior_de_tres(a: i32, b: i32, c: i32) -> i32 { let maior_ab = if a > b { a } else { b }; if maior_ab > c { maior_ab } else { c } }