Lezioni del modulo (1/2)
Generici e funzioni
I generics (tipi generici) consentono di scrivere codice flessibile e riutilizzabile, evitando la duplicazione di logica per tipi di dati differenti. Invece di definire più funzioni o strutture per tipi diversi (come i32, f64, o String), possiamo usare un parametro di tipo generico, convenzionalmente indicato con la lettera T.
Il compilatore Rust gestisce i generics tramite un processo chiamato monomorfizzazione: durante la compilazione, il compilatore genera una copia del codice generico per ciascun tipo concreto con cui viene effettivamente utilizzato. In questo modo non c'è alcun sovraccarico di prestazioni a runtime.
Funzioni Generiche
Per definire una funzione generica, inseriamo il parametro di tipo <T> subito dopo il nome della funzione e prima dell'elenco dei parametri:
fn print_value<T: std::fmt::Debug>(value: T) {
println!("Valore: {:?}", value);
}
Nelle funzioni generiche, possiamo usare il tipo generico T sia per i tipi degli argomenti che per il tipo di ritorno:
fn identity<T>(value: T) -> T {
value
}
Strutture Generiche
Possiamo utilizzare i parametri di tipo generico anche all'interno delle strutture dati (struct) per definire campi flessibili:
struct KeyValuePair<K, V> {
key: K,
value: V,
}
fn main() {
let pair = KeyValuePair {
key: String::from("eta"),
value: 30,
};
}
Nel blocco impl per una struct generica, dobbiamo dichiarare il parametro di tipo <T> subito dopo la parola chiave impl per indicare che stiamo implementando metodi su una struttura generica:
struct Container<T> {
value: T,
}
impl<T> Container<T> {
fn new(value: T) -> Self {
Container { value }
}
fn value(&self) -> &T {
&self.value
}
}
Prova tu
Esercizio 1: La struttura Point
Definisci una struttura generica chiamata Point<T> con due campi: x di tipo T e y di tipo T. Nel main, istanzia una variabile point contenente un Point con valori x pari a 5 e y pari a 10 (interi), quindi stampa a schermo il valore di point.x.
Mostra suggerimento
Dichiara la struct usando `struct Point<T> { x: T, y: T }`. Istanziala nel main e usa `point.x` per stamparla.
Soluzione disponibile dopo 3 tentativi
Esercizio 2: Inversione di una Tupla con swap
Scrivi una funzione generica chiamata swap<T> che accetta in input una tupla di due elementi (T, T) e restituisce una nuova tupla (T, T) con gli elementi invertiti di posizione. Nel main, chiama la funzione con la tupla (1, 2) e stampa il risultato.
Mostra suggerimento
La firma della funzione deve essere `fn swap<T>(pair: (T, T)) -> (T, T)`. Restituisci la tupla invertita con `(pair.1, pair.0)`.
Soluzione disponibile dopo 3 tentativi
Esercizio 3: La struttura Container
Definisci una struct generica Container<T> contenente un campo value di tipo T. Implementa un blocco impl generico per definire un metodo new che accetta un valore di tipo T e restituisce un'istanza di Container<T>.
Mostra suggerimento
Usa `impl<T> Container<T>`per implementare il metodo associato`fn new(value: T) -> Self { Container { value } }`.
Soluzione disponibile dopo 3 tentativi