Funções Genéricas
Parte da seção Programação Orientada a Objetos do Journey de Rust da Coddy. Lição 27 de 61.
Genéricos não se limitam 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 ainda deve ser flexível.
A sintaxe é semelhante à que você viu com structs. Declare o parâmetro genérico entre colchetes angulares após o nome da função e, em seguida, use-o nos parâmetros e no tipo de retorno:
fn identity<T>(value: T) -> T {
value
}
Essa função identity recebe um valor de qualquer tipo e o retorna inalterado. <T> declara o tipo genérico, value: T o aceita como parâmetro, e -> T especifica o tipo de retorno. Quando você chama a função, Rust infere o tipo concreto:
let num = identity(42); // T é i32
let text = identity("hello"); // T é &str
Você também pode usar vários 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 escrever uma lógica reutilizável uma vez e aplicá-la a muitos tipos, reduzindo a duplicação de código enquanto mantêm a segurança de tipos.
Desafio
FácilVamos criar um módulo utilitário com funções genéricas que podem trabalhar com qualquer tipo! Você criará funções independentes que demonstram como os genéricos tornam seu código flexível e reutilizável, sem vinculá-lo 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 retorna-os na ordem inversa 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 perfeitamente com inteiros, números de ponto flutuante e strings.
No seu arquivo principal, demonstre suas funções utilitárias:
- Usando
wrap_in_paircom um inteiro (primeira entrada, analisada comoi32) e imprimindo os dois elementos - Usando
wrap_in_paircom uma string (segunda entrada) e imprimindo os dois elementos - Usando
swapcom um inteiro (terceira entrada, analisada comoi32) e uma string (quarta entrada), e então imprimindo o resultado trocado
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 como 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.
Experimente você mesmo
mod utils;
use std::io;
fn main() {
// Leia 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
// Format: Pair of strings: ({value}, {value})
// TODO: Use swap com num2 e str2, então 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 Programação Orientada a Objetos
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 PlayerPratique por conta própria: Compilador de Rust online