Struct generiche
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 24 di 61.
Finora, ogni struct che hai creato aveva campi con tipi specifici e concreti. Un Pet ha un nome di tipo String, un Rectangle ha dimensioni di tipo f64. Ma cosa succede se vuoi creare una struct che possa contenere qualsiasi tipo di dato?
È qui che entrano in gioco i generici. I generici ti permettono di scrivere codice flessibile e riutilizzabile che funziona con più tipi. Invece di codificare un tipo specifico, usi un segnaposto, in genere T, che viene sostituito con un tipo concreto quando usi la struct.
Ecco una semplice struct wrapper che può contenere qualsiasi tipo:
struct Wrapper<T> {
value: T,
}
<T> dopo il nome della struct dichiara un parametro di tipo generico. All'interno della struct, T funge da segnaposto per il tipo che userai effettivamente. Quando crei un'istanza, Rust deduce il 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
Ognuno di questi è un tipo concreto diverso, Wrapper<i32>, Wrapper<f64> e Wrapper<&str>, ma condividono tutti la stessa definizione di struct. Questo elimina la necessità di scrivere struct separate IntWrapper, FloatWrapper e StringWrapper che fanno essenzialmente la stessa cosa.
La lettera T è solo una convenzione (abbreviazione di "Type"). Potresti usare qualsiasi identificatore valido, ma T è lo standard in Rust e nella maggior parte degli altri linguaggi con i generici.
Sfida
FacileCostruiamo un contenitore generico in grado di contenere qualsiasi tipo di valore! Creerai una struct Container che dimostra come i generics rendano il tuo codice flessibile e riutilizzabile.
Organizzerai il codice in due file:
container.rs: definisci una struct generica pubblica chiamataContainer<T>con un unico campo pubblicoitemdi tipoT. Questo contenitore deve poter contenere qualsiasi tipo: interi, numeri in virgola mobile, stringhe o qualsiasi altra cosa.main.rs: importa il modulo del contenitore e dimostrane la flessibilità creando contenitori che contengono diversi tipi di dati. Creerai tre contenitori e mostrerai cosa contiene ciascuno.
Nel file principale, crea i seguenti contenitori:
- Un contenitore che contiene un valore intero (il primo input, analizzato come
i32) - Un contenitore che contiene un valore in virgola mobile (il secondo input, analizzato come
f64) - Un contenitore che contiene una stringa (il terzo input, mantenuto come
String)
L'output deve mostrare il contenuto di ciascun contenitore in questo formato:
Integer container: {value}
Float container: {value}
String container: {value}Per esempio, con gli input 42, 3.14 e hello:
Integer container: 42
Float container: 3.14
String container: helloNota come la stessa definizione di Container<T> funzioni per tutti e tre i diversi tipi: questa è la potenza dei generics!
Riceverai tre input: un valore intero, un valore in virgola mobile e una stringa.
Provalo tu
mod container;
use container::Container;
fn main() {
// Leggi i tre input
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: Crea tre contenitori usando Container<T>
// 1. Un contenitore di interi che contiene int_value
// 2. Un contenitore di numeri in virgola mobile che contiene float_value
// 3. Un contenitore di stringhe che contiene string_value
// TODO: Stampa il contenuto di ciascun contenitore nel formato richiesto
// Integer container: {value}
// Float container: {value}
// String container: {value}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Metodi e comportamento
Introduzione ai blocchi implIl parametro selfMetodi mutabiliFunzioni associateBlocchi impl multipliConcatenamento di metodiRiepilogo - Azioni sul rettangolo4Progetto: animale domestico virtuale
Definire l'animaleNutrire l'animale7Trait standard
Il trait DebugIl trait DisplayClone e CopyTrait di uguaglianzaRiepilogo - Punto stampabile10Progetto: sistema di documenti
Il trait DrawComponente di testo2Incapsulamento e moduli
Nozioni di base sui moduliLa parola chiave publicCampi privatiGetterSetterRiepilogo - Armadietto sicuro5Generics
Struct genericheMetodi genericiPiù tipi genericiFunzioni genericheRipasso - Punto di coordinate8I Trait come vincoli
Sintassi dei vincoli sui traitVincoli multipliLa clausola whereRestituire tipi con i traitRiepilogo - Stampante generica11Modelli di progettazione in Rust
Pattern NewtypeComposizioneIl trait DropFrom e IntoRipasso - Mock con smart pointer3Enumerazioni avanzate
Enumerazioni con datiMetodi sulle enumerazioniCorrispondenza delle varianti con datiL'enumerazione Option ripresaRiepilogo - enumerazione Shape6Definizione dei trait
Che cos’è un trait?Implementare i traitImplementazioni predefiniteSovrascrivere le implementazioni predefiniteTrait con parametriRiepilogo - Lettore multimedialeEsercitati da solo: Compilatore Rust online