Múltiplos Tipos Genéricos
Parte da seção Programação Orientada a Objetos do Journey de Rust da Coddy. Lição 26 de 61.
Às vezes, um único parâmetro genérico não é suficiente. E se você precisar de uma struct que contenha dois valores, e esses valores puderem ser de tipos diferentes? Rust permite usar vários parâmetros genéricos, separando-os por vírgulas.
Aqui está uma struct Pair que contém dois valores de tipos potencialmente diferentes:
struct Pair<T, U> {
first: T,
second: U,
}
O <T, U> declara dois parâmetros de tipo independentes. T e U podem ser do mesmo tipo ou completamente diferentes: isso depende de você ao criar uma instância:
let both_ints = Pair { first: 10, second: 20 }; // T=i32, U=i32
let mixed = Pair { first: "hello", second: 3.14 }; // T=&str, U=f64
let reversed = Pair { first: 100, second: "world" }; // T=i32, U=&str
Ao implementar métodos, você declara todos os parâmetros genéricos no bloco impl:
impl<T, U> Pair<T, U> {
fn get_first(&self) -> &T {
&self.first
}
fn get_second(&self) -> &U {
&self.second
}
}
Esse padrão é comum quando você precisa associar duas partes de dados relacionadas, mas potencialmente diferentes, como um par chave-valor, coordenadas com tipos numéricos diferentes ou um resultado emparelhado com metadados.
Desafio
FácilVamos criar um sistema de armazenamento de chave-valor que demonstra o poder de vários tipos genéricos! Você criará uma struct KeyValue que pode combinar quaisquer dois tipos, perfeita para associar rótulos a dados, IDs a objetos ou qualquer outra combinação.
Você organizará seu código em dois arquivos:
keyvalue.rs: Defina uma struct genérica pública chamadaKeyValue<K, V>com dois campos privados:keydo tipoKevaluedo tipoV. Implemente métodos para trabalhar com essa struct:- Uma função associada
newque cria umKeyValuea partir de uma chave e um valor - Um método
keyque retorna uma referência à chave - Um método
valueque retorna uma referência ao valor - Um método
set_valueque substitui o valor atual por um novo
- Uma função associada
main.rs: Inclua seu módulo e demonstre como a mesma definição de struct funciona com diferentes combinações de tipos. Você criará pares de chave-valor com tipos mistos para mostrar a flexibilidade de vários genéricos.
No seu arquivo principal, crie e trabalhe com os seguintes pares de chave-valor:
- Um par com uma chave do tipo string e um valor inteiro (a primeira entrada como chave, a segunda entrada analisada como
i32como valor) - Imprima a chave e o valor
- Atualize o valor para um novo inteiro (a terceira entrada analisada como
i32) - Imprima o valor atualizado
- Crie um segundo par com uma chave inteira e um valor do tipo string (a quarta entrada analisada como
i32como chave, a quinta entrada como valor) - Imprima a chave e o valor desse par
Sua saída deve seguir este formato:
Key: {key}, Value: {value}
Updated value: {value}
Key: {key}, Value: {value}Por exemplo, com as entradas name, 42, 100, 7 e lucky:
Key: name, Value: 42
Updated value: 100
Key: 7, Value: luckyObserve como o primeiro par usa KeyValue<String, i32>, enquanto o segundo usa KeyValue<i32, String>: a mesma struct, diferentes combinações de tipos!
Você receberá cinco entradas: uma chave do tipo string, um valor inteiro, um inteiro de substituição, uma chave inteira e um valor do tipo string.
Experimente você mesmo
mod keyvalue;
use keyvalue::KeyValue;
fn main() {
// Leia cinco entradas
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let string_key = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let int_value: i32 = input2.trim().parse().expect("Failed to parse");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let replacement_int: i32 = input3.trim().parse().expect("Failed to parse");
let mut input4 = String::new();
std::io::stdin().read_line(&mut input4).expect("Failed to read line");
let int_key: i32 = input4.trim().parse().expect("Failed to parse");
let mut input5 = String::new();
std::io::stdin().read_line(&mut input5).expect("Failed to read line");
let string_value = input5.trim().to_string();
// TODO: Crie um par KeyValue<String, i32> usando string_key e int_value
// TODO: Imprima a chave e o valor no formato: "Key: {key}, Value: {value}"
// TODO: Atualize o valor usando set_value com replacement_int
// TODO: Print the updated value in format: "Updated value: {value}"
// TODO: Crie um par KeyValue<i32, String> usando int_key e string_value
// TODO: Imprima a chave e o valor deste par no formato: "Key: {key}, Value: {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