Per capire la funzione collect è importante sapere che serve a risolvere un iteratore. Ricorda che un iteratore è una sequenza di elementi che può essere concatenata con altri iteratori. Gli iteratori sono pigri: non eseguono nulla finché non chiami del codice che consuma l'iteratore. Tra questo codice ci sono i cicli for e gli adattatori che consumano. Gli adattatori che consumano elaborano tutti gli elementi dell'iteratore per produrre un qualche tipo di output.
collect è uno di questi adattatori che consumano: può essere usato per produrre un'istanza di qualsiasi tipo che implementa il trait FromIterator. Quel trait è implementato per tutte le collezioni della libreria standard. Dato che FromIterator è implementato per molti tipi, devi far sapere a Rust quale tipo di collezione vuoi. Per esempio, se vogliamo ottenere un Vec<char> da uno slice di stringa, possiamo inserire il tipo nella nostra chiamata a collect in questo modo:
let my_chars = "Hello, World!".chars().collect::<Vec<char>>();
// since the item type of the `String::chars()` iterator can be inferred to be `char`, we can omit that information
// let my_chars = "Hello, World!".chars().collect::<Vec<_>>();
I due punti doppi e le parentesi angolari esterne (cioè ::<_>) sono noti come turbofish. In questo caso il turbofish è una sintassi più rumorosa del necessario. Specificando il tipo direttamente sul binding, collect può inferire quale collezione creare con meno rumore sintattico.
let my_chars: Vec<char> = "Hello, World!".chars().collect();
// or
// let my_chars: Vec<_> = "Hello, World!".chars().collect();
Se volessimo una String con i caratteri invertiti, potremmo fare così:
let rev_chars: String = "Hello, World!".chars().rev().collect();
L'inferenza di tipo di Rust è abbastanza potente che a volte non serve un'annotazione esplicita inline:
fn get_reversed_chars(phrase: &str) -> Vec<char> {
phrase.chars().rev().collect()
}
Il tipo restituito dalla funzione get_reversed_chars permette alla funzione collect di inferire quale collezione restituire. Qui però dobbiamo specificare Vec<char> e non Vec<_>, perché Rust non esegue l'inferenza di tipo per le firme delle funzioni. Così chi legge il codice può capire rapidamente quali tipi una funzione consuma e restituisce.