Menu
Coddy logo textTech

Funções Genéricas

Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 27 de 61.

Generics não estão limitados a structs—você também pode escrever funções independentes que funcionam com qualquer tipo. Isso é útil quando você precisa de uma função utilitária que não pertence a uma struct específica, mas que ainda deve ser flexível.

A sintaxe reflete o que você viu com structs. Declare o parâmetro genérico em colchetes angulares após o nome da função, então use-o nos parâmetros e no tipo de retorno:

fn identity<T>(value: T) -> T {
    value
}

Esta função identity recebe um valor de qualquer tipo e o retorna inalterado. O <T> declara o genérico, value: T o aceita como um parâmetro, e -> T especifica o tipo de retorno. Quando você chama a função, o Rust infere o tipo concreto:

let num = identity(42);        // T é i32
let text = identity("hello");  // T é &str

Você também pode usar múltiplos parâmetros genéricos em funções, assim como em structs:

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // retorna (i32, &str)

Funções genéricas permitem que você escreva lógica reutilizável uma única vez e a aplique a muitos tipos, reduzindo a duplicação de código enquanto mantém a segurança de tipos.

challenge icon

Desafio

Fácil

Vamos construir um módulo utilitário com funções genéricas que podem funcionar com qualquer tipo! Você criará funções independentes que demonstram como os genéricos tornam seu código flexível e reutilizável sem estar vinculado a uma struct específica.

Você organizará seu código em dois arquivos:

  • utils.rs: Crie uma coleção de funções utilitárias genéricas públicas:
    • wrap_in_pair<T> — recebe um único valor e retorna uma tupla contendo esse valor duas vezes: (value, value). Isso exige que o tipo seja Clone, então use <T: Clone>
    • swap<T, U> — recebe dois valores de tipos potencialmente diferentes e os retorna em ordem invertida como uma tupla (U, T)
  • main.rs: Importe seu módulo utilitário e demonstre essas funções genéricas funcionando com diferentes tipos. Mostre como as mesmas definições de função lidam com inteiros, floats e strings perfeitamente.

No seu arquivo principal, demonstre suas funções utilitárias:

  1. Usando wrap_in_pair com um inteiro (primeira entrada, analisada como i32) e imprimindo ambos os elementos
  2. Usando wrap_in_pair com uma string (segunda entrada) e imprimindo ambos os elementos
  3. Usando swap com um inteiro (terceira entrada, analisada como i32) e uma string (quarta entrada), e então imprimindo o resultado invertido

Sua saída deve seguir este formato:

Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})

Por exemplo, com as entradas 5, hello, 42, e world:

Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)

Observe como wrap_in_pair funciona de forma idêntica tanto para inteiros quanto para strings, e swap lida com dois tipos completamente diferentes — essa é a flexibilidade das funções genéricas!

Você receberá quatro entradas: um inteiro, uma string, outro inteiro e outra string.

Folha de consulta

Funções genéricas permitem que você escreva funções utilitárias independentes que funcionam com qualquer tipo. Declare o parâmetro genérico entre colchetes angulares após o nome da função:

fn identity<T>(value: T) -> T {
    value
}

O <T> declara o parâmetro de tipo genérico, que pode então ser usado nos parâmetros da função e nos tipos de retorno. O Rust infere o tipo concreto quando você chama a função:

let num = identity(42);        // T é i32
let text = identity("hello");  // T é &str

Você pode usar múltiplos parâmetros genéricos em uma única função:

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // retorna (i32, &str)

Quando um tipo genérico precisa de capacidades específicas, use limites de trait (trait bounds):

fn duplicate<T: Clone>(value: T) -> (T, T) {
    (value.clone(), value)
}

Experimente você mesmo

mod utils;

use std::io;

fn main() {
    // Lê as quatro entradas
    let mut input1 = String::new();
    io::stdin().read_line(&mut input1).expect("Failed to read line");
    let num1: i32 = input1.trim().parse().expect("Invalid integer");

    let mut input2 = String::new();
    io::stdin().read_line(&mut input2).expect("Failed to read line");
    let str1 = input2.trim().to_string();

    let mut input3 = String::new();
    io::stdin().read_line(&mut input3).expect("Failed to read line");
    let num2: i32 = input3.trim().parse().expect("Invalid integer");

    let mut input4 = String::new();
    io::stdin().read_line(&mut input4).expect("Failed to read line");
    let str2 = input4.trim().to_string();

    // TODO: Use wrap_in_pair com num1 e imprima o resultado
    // Formato: Pair of ints: ({value}, {value})

    // TODO: Use wrap_in_pair com str1 e imprima o resultado
    // Formato: Pair of strings: ({value}, {value})

    // TODO: Use swap com num2 e str2, depois imprima o resultado trocado
    // Formato: Swapped: ({string}, {int})
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming