Funzioni generiche
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 27 di 61.
I generici non sono limitati alle struct. Puoi anche scrivere funzioni indipendenti che funzionano con qualsiasi tipo. Questo è utile quando ti serve una funzione di utilità che non appartiene a una struct specifica ma dovrebbe comunque essere flessibile.
La sintassi rispecchia quella che hai visto con le struct. Dichiara il parametro generico tra parentesi angolari dopo il nome della funzione, poi usalo nei parametri e nel tipo restituito:
fn identity<T>(value: T) -> T {
value
}
Questa funzione identity accetta un valore di qualsiasi tipo e lo restituisce invariato. <T> dichiara il tipo generico, value: T lo accetta come parametro e -> T specifica il tipo restituito. Quando chiami la funzione, Rust deduce il tipo concreto:
let num = identity(42); // T è i32
let text = identity("hello"); // T è &str
Puoi anche usare più parametri generici nelle funzioni, proprio come con le struct:
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // restituisce (i32, &str)
Le funzioni generiche ti permettono di scrivere una logica riutilizzabile una sola volta e applicarla a molti tipi, riducendo la duplicazione del codice e mantenendo la sicurezza dei tipi.
Sfida
FacileCostruiamo un modulo di utilità con funzioni generiche che possono funzionare con qualsiasi tipo! Creerai funzioni autonome che mostrano come i generici rendano il tuo codice flessibile e riutilizzabile, senza vincolarlo a una struttura specifica.
Organizzerai il codice in due file:
utils.rs: crea una raccolta di funzioni di utilità generiche pubbliche:wrap_in_pair<T>: accetta un singolo valore e restituisce una tupla contenente due volte quel valore:(value, value). Il tipo deve implementareClone, quindi usa<T: Clone>swap<T, U>: accetta due valori potenzialmente di tipi diversi e li restituisce in ordine inverso come tupla(U, T)
main.rs: importa il tuo modulo di utilità e mostra queste funzioni generiche all'opera con tipi diversi. Mostra come le stesse definizioni di funzione gestiscono senza problemi interi, numeri a virgola mobile e stringhe.
Nel tuo file principale, mostra le tue funzioni di utilità:
- Usa
wrap_in_paircon un intero (primo input, analizzato comei32) e stampa entrambi gli elementi - Usa
wrap_in_paircon una stringa (secondo input) e stampa entrambi gli elementi - Usa
swapcon un intero (terzo input, analizzato comei32) e una stringa (quarto input), quindi stampa il risultato scambiato
Il tuo output deve rispettare questo formato:
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})Per esempio, con gli input 5, hello, 42 e world:
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)Nota come wrap_in_pair funzioni in modo identico sia con gli interi sia con le stringhe, e come swap gestisca due tipi completamente diversi: questa è la flessibilità delle funzioni generiche!
Riceverai quattro input: un intero, una stringa, un altro intero e un'altra stringa.
Provalo tu
mod utils;
use std::io;
fn main() {
// Leggi i quattro input
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: Usa wrap_in_pair con num1 e stampa il risultato
// Formato: Pair of ints: ({value}, {value})
// TODO: Usa wrap_in_pair con str1 e stampa il risultato
// Formato: Pair of strings: ({value}, {value})
// TODO: Usa swap con num2 e str2, poi stampa il risultato dopo lo scambio
// Formato: Scambiati: ({string}, {int})
}
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