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.
Desafio
FácilVamos 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 sejaClone, 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:
- Usando
wrap_in_paircom um inteiro (primeira entrada, analisada comoi32) e imprimindo ambos os elementos - Usando
wrap_in_paircom uma string (segunda entrada) e imprimindo ambos os elementos - Usando
swapcom um inteiro (terceira entrada, analisada comoi32) 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})
}
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
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player