Structs Genéricas
Parte da seção Programação Orientada a Objetos do Journey de Rust da Coddy. Lição 24 de 61.
Até agora, toda struct que você criou teve campos com tipos específicos e concretos. Uma Pet tem um nome String, uma Rectangle tem dimensões f64. Mas e se você quiser criar uma struct que possa armazenar qualquer tipo de dado?
É aqui que entram os genéricos. Os genéricos permitem escrever código flexível e reutilizável que funciona com vários tipos. Em vez de codificar um tipo específico diretamente, você usa um marcador, normalmente T, que é substituído por um tipo concreto quando você usa a struct.
Aqui está uma struct wrapper simples que pode conter qualquer tipo:
struct Wrapper<T> {
value: T,
}
O <T> após o nome da struct declara um parâmetro de tipo genérico. Dentro da struct, T atua como um substituto para qualquer tipo que você realmente usará. Quando você cria uma instância, o Rust infere o tipo concreto:
let int_wrapper = Wrapper { value: 42 }; // T é i32
let float_wrapper = Wrapper { value: 3.14 }; // T é f64
let text_wrapper = Wrapper { value: "hello" }; // T é &str
Cada um deles é um tipo concreto diferente, Wrapper<i32>, Wrapper<f64> e Wrapper<&str>, mas todos compartilham a mesma definição de struct. Isso elimina a necessidade de escrever structs IntWrapper, FloatWrapper e StringWrapper separadas que fazem essencialmente a mesma coisa.
A letra T é apenas uma convenção (abreviação de "Tipo"). Você poderia usar qualquer identificador válido, mas T é padrão em Rust e na maioria das outras linguagens com genéricos.
Desafio
FácilVamos criar um contêiner genérico que pode armazenar qualquer tipo de valor! Você criará uma struct Container que demonstra como os genéricos tornam seu código flexível e reutilizável.
Você organizará seu código em dois arquivos:
container.rs: Defina uma struct genérica pública chamadaContainer<T>com um único campo públicoitemdo tipoT. Esse contêiner deve ser capaz de armazenar qualquer tipo: inteiros, números de ponto flutuante, strings ou qualquer outra coisa.main.rs: Importe seu módulo de contêiner e demonstre sua flexibilidade criando contêineres que armazenam diferentes tipos de dados. Você criará três contêineres e exibirá o que cada um armazena.
Em seu arquivo principal, crie os seguintes contêineres:
- Um contêiner contendo um valor inteiro (a primeira entrada, analisada como
i32) - Um contêiner contendo um valor de ponto flutuante (a segunda entrada, analisada como
f64) - Um contêiner contendo uma string (a terceira entrada, mantida como uma
String)
Sua saída deve exibir o conteúdo de cada contêiner neste formato:
Integer container: {value}
Float container: {value}
String container: {value}Por exemplo, com as entradas 42, 3.14 e hello:
Integer container: 42
Float container: 3.14
String container: helloObserve como a mesma definição de Container<T> funciona para os três tipos diferentes: esse é o poder dos genéricos!
Você receberá três entradas: um valor inteiro, um valor de ponto flutuante e uma string.
Experimente você mesmo
mod container;
use container::Container;
fn main() {
// Ler as três entradas
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let int_value: i32 = input1.trim().parse().expect("Failed to parse integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let float_value: f64 = input2.trim().parse().expect("Failed to parse float");
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: Create three containers using Container<T>
// 1. An integer container holding int_value
// 2. A float container holding float_value
// 3. A string container holding string_value
// TODO: Imprimir o conteúdo de cada container no formato requerido
// Integer container: {value}
// Float container: {value}
// String container: {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 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