Rust Admin

O que Todo Dev Deve Saber sobre Funções, Expressões e o Sistema de Tipos de Rust Já leu

Funções em Rust: Sintaxe, Parâmetros e Retornos Funções são a base da organização de código em Rust. Diferente de muitas linguagens, Rust distingue claramente entre instruções (que não retornam valor) e expressões (que retornam). Uma função é declarada com a palavra-chave , seguida de um nome, parâmetros entre parênteses e, opcionalmente, um tipo de retorno precedido por . Note que em Rust, a última linha sem é uma expressão que retorna seu valor. Se adicionar , torna-se uma instrução que retorna (unit type). Os parâmetros exigem anotação de tipo — isso é obrigatório e não há tipo padrão inferido. Funções podem retornar múltiplos valores usando tuplas, e o compilador força a tratativa correta de tipos, eliminando bugs em tempo de compilação. Closures e Funções Anônimas Closures são funções sem nome que capturam variáveis do escopo envolvente. São poderosas para programação funcional e callbacks. A sintaxe é , onde os tipos podem ser inferidos ou explícitos. Closures implementam os traits ,

Funções em Rust: Sintaxe, Parâmetros e Retornos

Funções são a base da organização de código em Rust. Diferente de muitas linguagens, Rust distingue claramente entre instruções (que não retornam valor) e expressões (que retornam). Uma função é declarada com a palavra-chave fn, seguida de um nome, parâmetros entre parênteses e, opcionalmente, um tipo de retorno precedido por ->.

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

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

fn main() {
    let result = add(5, 3);
    println!("Resultado: {}", result);
    greet("Maria");
}

Note que em Rust, a última linha sem ; é uma expressão que retorna seu valor. Se adicionar ;, torna-se uma instrução que retorna () (unit type). Os parâmetros exigem anotação de tipo — isso é obrigatório e não há tipo padrão inferido. Funções podem retornar múltiplos valores usando tuplas, e o compilador força a tratativa correta de tipos, eliminando bugs em tempo de compilação.

Closures e Funções Anônimas

Closures são funções sem nome que capturam variáveis do escopo envolvente. São poderosas para programação funcional e callbacks. A sintaxe é |parametros| { corpo }, onde os tipos podem ser inferidos ou explícitos.

fn main() {
    let num = 5;
    let add_num = |x| x + num; // Captura 'num' por referência

    println!("{}", add_num(3)); // Imprime 8

    let multiply = |a: i32, b: i32| -> i32 { a * b };
    println!("{}", multiply(4, 2)); // Imprime 8

    let values = vec![1, 2, 3, 4];
    let doubled: Vec<i32> = values.iter().map(|x| x * 2).collect();
    println!("{:?}", doubled); // [2, 4, 6, 8]
}

Closures implementam os traits Fn, FnMut ou FnOnce, dependendo de como capturam variáveis. Fn captura por referência imutável, FnMut por referência mutável, e FnOnce consome a variável. O compilador infere automaticamente qual trait usar.

Expressões: O Coração de Rust

Em Rust, quase tudo é uma expressão. Blocos {}, loops, condicionais — todos retornam valores. Isso diferencia Rust de linguagens como C ou Java, onde essas estruturas são apenas instruções. Uma expressão não termina com ponto-e-vírgula; se terminar, vira uma instrução.

fn main() {
    // Expressão if
    let number = 5;
    let result = if number > 0 {
        "positivo"
    } else {
        "não positivo"
    };
    println!("{}", result); // "positivo"

    // Expressão match
    let value = 42;
    let message = match value {
        0 => "zero",
        1..=10 => "pequeno",
        _ => "grande",
    };
    println!("{}", message); // "grande"

    // Bloco como expressão
    let x = {
        let y = 5;
        let z = 6;
        y + z // Sem ; retorna o valor
    };
    println!("{}", x); // 11
}

Esse design força o programador a pensar em fluxo de dados de forma mais explícita. A ausência de um ; é sintaticamente significativa e intencional, evitando retornos acidentais de ().

O Sistema de Tipos: Estático, Forte e Inteligente

Rust possui um sistema de tipos estático e forte, onde cada valor tem um tipo conhecido em tempo de compilação. O sistema é tão rigoroso que evita classes inteiras de bugs. Além dos tipos primitivos (i32, f64, bool, &str), Rust oferece tipos compostos, genéricos e traits para abstraçãoe reutilização.

Tipos Genéricos e Traits

Genéricos permitem escrever código reutilizável sem sacrificar segurança de tipo. Traits definem comportamentos compartilhados entre tipos diferentes, funcionando como interfaces.

// Função genérica
fn print_it<T: std::fmt::Display>(val: T) {
    println!("Valor: {}", val);
}

// Trait customizado
trait Animal {
    fn fazer_som(&self) -> &str;
}

struct Cachorro;
struct Gato;

impl Animal for Cachorro {
    fn fazer_som(&self) -> &str {
        "Au au!"
    }
}

impl Animal for Gato {
    fn fazer_som(&self) -> &str {
        "Miau!"
    }
}

fn main() {
    print_it(42);
    print_it("Olá");

    let dog = Cachorro;
    let cat = Gato;

    println!("{}", dog.fazer_som()); // Au au!
    println!("{}", cat.fazer_som()); // Miau!
}

O bound T: std::fmt::Display garante que T implementa o trait Display. Isso permite que o compilador verifique segurança sem runtime overhead. Traits também viabilizam polimorfismo sem herança, seguindo composição sobre herança.

Option e Result: Tratamento de Erros Seguro

Rust não possui null; ao invés, usa Option<T> para valores opcionais e Result<T, E> para operações que podem falhar. Isso força o tratamento explícito de casos de erro.

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err("Divisão por zero!".to_string())
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10, 2) {
        Ok(resultado) => println!("Resultado: {}", resultado),
        Err(erro) => println!("Erro: {}", erro),
    }

    // Sintaxe '?' para propagação de erro
    let result = divide(20, 5).unwrap_or(0);
    println!("{}", result); // 4
}

O operador ? propaga erros automaticamente, reduzindo boilerplate. unwrap() extrai o valor ou panics se for erro; use apenas quando tiver certeza. Essa abordagem torna tratamento de erro explícito e impossível de ignorar.

Conclusão

Dominando funções, expressões e o sistema de tipos de Rust, você terá acesso à filosofia central da linguagem: segurança em tempo de compilação sem sacrificar performance. Primeira lição: funções e closures são ferramentas poderosas para composição de código. Segunda: expressões, não instruções, levam a um código mais funcional e previsível. Terceira: o sistema de tipos não é um obstáculo — é seu aliado contra bugs, forçando você a pensar claramente sobre possibilidades de erro e fluxo de dados.

Referências


Artigos relacionados