Errore Rust E0631: type mismatch in closure arguments (tipo dei parametri)
E0631 significa che la tua closure ha il numero giusto di parametri, ma almeno uno ha il tipo sbagliato. Nove volte su dieci la differenza è un solo &: hai scritto |x: i32|, ma l’iteratore ti presta un &i32. Spesso la correzione più veloce è cancellare l’annotazione di tipo e lasciare che Rust lo capisca da solo. Questa pagina mostra prima quella soluzione, poi spiega da dove arriva il &, così la prossima volta te lo aspetti. Per gli altri errori di questa serie, vedi Errori del compilatore Rust.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0631]: type mismatch in closure arguments --> src/main.rs:3:43 |3 | let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ items are &i32 | ^^^ -------- found signature defined here | | | expected due to this | = note: expected closure signature `fn(&{integer}) -> _` found closure signature `fn(i32) -> _`note: required by a bound in `map`help: consider adjusting the signature so it borrows its argument |3 | let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); // ❌ items are &i32 | +Il compilatore ti dice: «tipi non corrispondenti negli argomenti della closure». Ti mostra la firma che si aspettava, quella che ha trovato, e ti suggerisce di far ricevere alla closure un riferimento.
Ecco il programma che lo provoca:
fn main() { let prices = vec![120, 45, 300]; let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ items are &i32 println!("{:?}", doubled);}(Il commento dice: gli elementi sono &i32.)
Subito dopo, rustc stampa anche un secondo errore, più lungo: the method `collect` exists for struct `Map<...>`, but its trait bounds were not satisfied (E0599). È solo un effetto collaterale. Risolvi E0631 e sparisce.
Cosa significa
Sezione intitolata “Cosa significa”Le due righe note sono il cuore del messaggio. Leggile come «il metodo voleva questo, tu hai scritto quello»:
- expected
fn(&{integer}) -> _:mapchiamerà la tua closure con un riferimento a un intero.{integer}vuol dire «un tipo intero che Rust non ha ancora fissato», e_vuol dire «qualsiasi tipo di ritorno». - found
fn(i32) -> _: la tua closure dice di ricevere uni32per valore.
Mettile una accanto all’altra e l’unica differenza è il &. La riga help: mostra la correzione di un solo carattere.
Pensa al guardaroba di un teatro. Lasci il cappotto e ricevi un tagliando. .iter() funziona allo stesso modo: non ti dà gli elementi, ti dà dei tagliandi per raggiungerli (i riferimenti). Una closure che pretende il cappotto in persona non accetta un tagliando.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Ogni metodo degli iteratori decide esattamente quale tipo passare alla tua closure, e quel tipo dipende da come hai creato l’iteratore:
v.iter()presta ogni elemento:&T.v.into_iter()(su unVecche possiedi) cede ogni elemento:T.filterefindti prestano l’elemento un’altra volta, quindi sopra a.iter()ottieni&&T.sort_byti presta due elementi:&Te&T.
Quando ometti i tipi dei parametri, Rust li prende dal metodo, quindi vanno sempre bene. Quando scrivi un tipo tu, Rust ti prende in parola e lo controlla. Non copia il valore di nascosto e non trasforma un riferimento in un valore posseduto, perché cambierebbe il comportamento del tuo codice (e per una String una copia costa memoria e tempo). La lezione Iteratori mostra i tre modi per ottenere un iteratore, e Prestito (borrowing) spiega i riferimenti.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: togli l’annotazione di tipo
Sezione intitolata “Soluzione 1: togli l’annotazione di tipo”Prova questa per prima. I parametri delle closure non hanno quasi mai bisogno di un tipo: Rust sa esattamente cosa passano map, filter e sort_by. Cancella : i32 e il codice compila:
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// nessuna annotazione di tipo: Rust usa ciò che l'iteratore dà davvero let doubled: Vec<i32> = prices.iter().map(|p| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age| **age >= 18).collect(); scores.sort_by(|a, b| b.cmp(a));
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores);}Output:
[240, 90, 600][34, 50][99, 42, 15, 7]p * 2 funziona su un &i32 perché gli operatori numerici di Rust accettano anche i riferimenti. In filter, age è un &&i32, quindi **age segue entrambi i riferimenti fino al numero. I confronti come b.cmp(a) funzionano direttamente sui riferimenti.
Soluzione 2: scrivi il tipo che il metodo passa davvero
Sezione intitolata “Soluzione 2: scrivi il tipo che il metodo passa davvero”Scegli questa strada quando vuoi tenere il tipo in vista, per esempio per rendere più leggibile una catena lunga. Segui l’help: di rustc e aggiungi il &. Puoi anche mettere il & nel pattern: |&p| scompone il riferimento e ti dà una semplice copia del numero (funziona con i tipi Copy come gli interi):
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// scrivi i tipi veri: & per .iter(), && dentro filter let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age: &&i32| **age >= 18).collect(); scores.sort_by(|a: &i32, b: &i32| b.cmp(a));
// oppure copia fuori il numero con un pattern & let tax: Vec<i32> = prices.iter().map(|&p| p / 10).collect();
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores); println!("{:?}", tax);}Output:
[240, 90, 600][34, 50][99, 42, 15, 7][12, 4, 30]Nota la differenza: |p: &i32| vuol dire «p è un riferimento», mentre |&p| vuol dire «ricevo un riferimento, dammi quello che c’è dietro».
Soluzione 3: cambia quello che l’iteratore distribuisce
Sezione intitolata “Soluzione 3: cambia quello che l’iteratore distribuisce”Scegli questa strada quando vuoi davvero i valori, non i riferimenti. Hai due modi per ottenerli:
.iter().copied()trasforma ogni&i32in uni32. Usalo per piccoli tipiCopycome numeri,charebool. (.cloned()fa lo stesso per tipi comeString, clonandoli.).into_iter()su unVecdistribuisce gli elementi stessi. Dopo, ilVecè consumato, quindi sceglilo quando non ti serve più.
fn main() { let prices = vec![120, 45, 300];
// .copied() trasforma gli elementi &i32 in elementi i32 let doubled: Vec<i32> = prices.iter().copied().map(|p: i32| p * 2).collect(); println!("{:?}", doubled);
// .into_iter() su un Vec distribuisce gli elementi stessi (e consuma il Vec) let names = vec![String::from("ferris"), String::from("corro")]; let loud: Vec<String> = names.into_iter().map(|name: String| name.to_uppercase()).collect(); println!("{:?}", loud);}Output:
[240, 90, 600]["FERRIS", "CORRO"]Se dopo il ciclo ti serve ancora names, tieni .iter() e ricevi un &String. Spostare elementi fuori da un Vec che hai solo preso in prestito è un altro errore, E0507.
Soluzione 4: fai ricevere a una funzione con nome ciò che dà l’iteratore
Sezione intitolata “Soluzione 4: fai ricevere a una funzione con nome ciò che dà l’iteratore”Scegli questa strada quando passi una funzione per nome, come .map(shout). In questo caso il messaggio dice «function» invece di «closure»:
error[E0631]: type mismatch in function arguments --> src/main.rs:7:51 |1 | fn shout(word: &String) -> String { | --------------------------------- found signature defined here...7 | let loud: Vec<String> = words.into_iter().map(shout).collect(); | --- ^^^^^ expected due to this | | | required by a bound introduced by this call | = note: expected function signature `fn(&str) -> _` found function signature `fn(&String) -> _`Gli elementi sono &str, ma shout vuole un &String. Una buona abitudine: le funzioni che leggono solo del testo ricevano &str. Così accettano letterali stringa, &String e altro ancora. Quando l’iteratore aggiunge un & in più (come fa filter), avvolgi la chiamata in una piccola closure. In una normale chiamata di funzione Rust toglie da solo i & di troppo (si chiama deref coercion), ma non può farlo quando passi la funzione per nome:
// ricevi &str: funziona con letterali stringa, &String e altrofn shout(word: &str) -> String { word.to_uppercase()}
fn is_long(word: &str) -> bool { word.len() > 4}
fn main() { let words = vec!["crab", "rustacean", "ferris"];
// gli elementi sono &str, quindi shout si può passare per nome let loud: Vec<String> = words.iter().copied().map(shout).collect(); println!("{:?}", loud);
// filter dà &&str: una piccola closure lascia a Rust la conversione let long: Vec<&str> = words.iter().copied().filter(|w| is_long(w)).collect(); println!("{:?}", long);}Output:
["CRAB", "RUSTACEAN", "FERRIS"]["rustacean", "ferris"]Qui le parole restano in inglese perché is_long conta i byte con .len(). Trovi di più su &str e String in Stringhe.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Ordinare con parametri tipizzati. scores.sort_by(|a: i32, b: i32| b.cmp(&a)) dà E0631 con expected closure signature `for<'a, 'b> fn(&'a {integer}, &'b {integer}) -> _`. La parte for<'a, 'b> dice solo «per qualsiasi prestito». Soluzione: |a, b| b.cmp(a). Vedi Ordinamento.
Voci di una mappa con tipi posseduti. stock.iter().map(|(name, count): (String, i32)| ...) su una HashMap<String, i32> dà expected closure signature `fn((&String, &_)) -> _`. .iter() su una mappa presta sia la chiave sia il valore. Soluzione: togli l’annotazione, oppure scrivi (&String, &i32).
Una tua funzione generica. Se fn apply<F: Fn(i32) -> i32>(f: F, x: i32) riceve una closure |x: f64| x / 2.0, rustc segnala expected closure signature `fn(i32) -> _` e found closure signature `fn(f64) -> _`, indicando il vincolo. Soluzione: fai ricevere i32 alla closure (e converti dentro, x as f64 / 2.0), oppure cambia il vincolo se intendevi davvero f64. Vedi Trait delle closure.
for_each con String. words.iter().for_each(|w: String| ...) su un Vec<&str> si aspetta fn(&&str). Un &str non è una String. Soluzione: |w|, oppure |w: &&str|.
Errori correlati
Sezione intitolata “Errori correlati”- E0593: closure takes the wrong number of arguments: l’errore gemello, per il numero di parametri (per esempio
|a, b|quando arriva una sola tupla). - E0057: closure called with the wrong number of arguments: chiami tu una closure e le passi troppi o troppo pochi valori.
- E0308: mismatched types: l’errore generico «tipo sbagliato», per esempio quando il valore restituito dalla closure ha il tipo sbagliato, o con un parametro puntatore
fn(...). - E0599: no method found: l’errore «trait bounds were not satisfied» su
collectosumche segue E0631. Sparisce insieme a E0631. - E0507: cannot move out of a borrowed value: quando provi a prendere possesso di elementi che hai solo preso in prestito con
.iter(). - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. let v = vec![1, 2, 3]; Che tipo riceve la closure in v.iter().map(|x| ...)?
2. La nota dice expected closure signature fn(&{integer}) -> _, found closure signature fn(i32) -> _. Qual è la correzione più veloce?
3. Cosa fa |&p| p * 2 dentro prices.iter().map(...)?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0631 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Processing a Series of Items with Iterators — The Rust Book (in inglese):
iter,into_itereiter_mut, e cosa distribuisce ciascuno. - Closures — The Rust Book (in inglese): come vengono dedotti i tipi dei parametri delle closure, e quando conviene scriverli.
Iterator::copied— documentazione della libreria standard (in inglese): trasforma un iteratore di&Tin un iteratore diT.slice::sort_by— documentazione della libreria standard (in inglese): la firma che mostra come la closure di confronto riceva due riferimenti.