Métodos Genéricos
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 25 de 61.
Uma struct genérica pode conter qualquer tipo, mas não é muito útil sem métodos para interagir com seus dados. Para definir métodos para uma struct genérica, você precisa de uma sintaxe especial no bloco impl.
A chave é declarar o parâmetro genérico no próprio impl:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
Observe o impl<T> antes de Wrapper<T>. Isso informa ao Rust que T é um parâmetro de tipo genérico para todo o bloco de implementação. Sem essa declaração, o Rust procuraria por um tipo concreto chamado T e falharia ao encontrá-lo.
O método get retorna &T—uma referência a qualquer tipo que o wrapper contenha. Isso funciona independentemente de T ser um inteiro, uma string ou qualquer outro tipo:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
A mesma definição de método funciona para ambos, porque o T genérico se adapta a cada tipo concreto em tempo de compilação.
Desafio
FácilVamos estender seu contêiner genérico com métodos! Você construirá uma struct Box (não confundir com a Box padrão do Rust) que pode conter qualquer tipo e fornece métodos para interagir com seu conteúdo.
Você organizará seu código em dois arquivos:
mybox.rs: Defina uma struct genérica pública chamadaMyBox<T>com um campo privadocontentsdo tipoT. Implemente métodos para esta struct:- Uma função associada
newque cria um novoMyBoxcom o valor fornecido - Um método
peekque retorna uma referência ao conteúdo (usando&self) - Um método
replaceque recebe um novo valor e substitui o conteúdo atual (usando&mut self)
- Uma função associada
main.rs: Importe seu módulo e demonstre os métodos genéricos funcionando com diferentes tipos. Você criará boxes, espiará seu conteúdo e substituirá valores para mostrar os métodos em ação.
Lembre-se da sintaxe principal para implementar métodos em uma struct genérica: você precisa de impl<T> antes de MyBox<T> para dizer ao Rust que T é um parâmetro genérico para todo o bloco de implementação.
No seu arquivo principal, demonstre seu MyBox ao:
- Criar um box com um número inteiro (primeira entrada, analisada como
i32) - Espiar seu conteúdo e imprimir o valor
- Substituir o conteúdo por um novo número inteiro (segunda entrada, analisada como
i32) - Espiar novamente para mostrar o valor atualizado
- Criar um segundo box com uma string (terceira entrada)
- Espiar o conteúdo do box de string
Sua saída deve seguir este formato:
Integer box contains: {value}
After replace: {value}
String box contains: {value}Por exemplo, com as entradas 10, 25 e Rust:
Integer box contains: 10
After replace: 25
String box contains: RustVocê receberá três entradas: um número inteiro inicial, um número inteiro de substituição e um valor de string.
Folha de consulta
Para definir métodos para uma struct genérica, declare o parâmetro genérico no bloco impl:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
A sintaxe impl<T> informa ao Rust que T é um parâmetro de tipo genérico para todo o bloco de implementação. Sem isso, o Rust procuraria por um tipo concreto chamado T.
Métodos podem retornar referências ao tipo genérico (&T), o que funciona para qualquer tipo concreto:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
Experimente você mesmo
mod mybox;
use mybox::MyBox;
fn main() {
// Ler as entradas
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let initial_int: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let replacement_int: i32 = input2.trim().parse().expect("Invalid integer");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let string_value = input3.trim().to_string();
// TODO: Criar um MyBox com o inteiro inicial
// TODO: Espiar seu conteúdo e imprimir: "Integer box contains: {value}"
// TODO: Substituir o conteúdo pelo inteiro de substituição
// TODO: Espiar novamente e imprimir: "After replace: {value}"
// TODO: Criar um segundo MyBox com o valor da string
// TODO: Espiar a caixa de string e imprimir: "String box contains: {value}"
}
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