Errore Rust E0107: takes N generic arguments but M were supplied (argomenti generici)
E0107 significa che il numero di tipi dentro <...> non corrisponde a quello che il tipo o la funzione si aspetta. Result ne vuole due (Result<T, E>), HashMap ne vuole due (HashMap<K, V>), Option e Vec ne vogliono uno. Se ne dai uno in meno o uno in più, rustc dice qualcosa come enum takes 2 generic arguments but 1 generic argument was supplied (l’enum prende 2 argomenti generici ma ne è stato fornito 1), con davanti «struct», «enum», «type alias», «function» o «method». È uno degli errori più gentili: rustc ti dice entrambi i numeri, e la correzione è aggiungere o togliere un tipo. Fa parte della guida agli errori del compilatore.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied --> src/main.rs:1:29 |1 | fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types | ^^^^^^ --- supplied 1 generic argument | | | expected 2 generic arguments |help: add missing generic argument |1 | fn parse_age(text: &str) -> Result<u32, E> { // ❌ `Result` needs two types | +++Il compilatore dice: l’enum Result prende 2 argomenti generici, ma ne hai fornito solo 1. Questo è il programma che produce l’errore:
fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types text.trim().parse()}
fn main() { for input in ["42", "forty"] { match parse_age(input) { Ok(age) => println!("age: {}", age), Err(e) => println!("error: {}", e), } }}Cosa significa
Sezione intitolata “Cosa significa”rustc indica due volte la riga 1:
- Il
^^^^^^sottoResult: «expected 2 generic arguments» (attesi 2 argomenti generici).Resultè un enum con due parametri di tipo:Tper il valore in caso di successo edEper l’errore. - Il
---sottou32: «supplied 1 generic argument» (fornito 1 argomento generico). Hai dato soloT. - L’
help:aggiunge un segnapostoE. È un’indicazione su dove va il tipo, non il tipo stesso: sostituisciEcon il vero tipo di errore.
Gli argomenti generici sono come gli spazi vuoti di un modulo. Il modulo Result ha due spazi, «se funziona:» e «se fallisce:». Se ne compili solo uno, l’impiegato te lo restituisce.
Questo capita spesso se hai letto codice che scrive Result<u32> con un solo tipo. Quel codice non era sbagliato: usava un alias di tipo come io::Result<T> o anyhow::Result<T>, che compila per te il tipo di errore. Il semplice Result del prelude non ha questa scorciatoia.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Un tipo generico non è un tipo completo finché ogni parametro non ha un valore. Result<u32, E> con una E sconosciuta non dice quanto è grande il valore né com’è fatto un errore, quindi Rust non può compilare il codice che lo usa. Rust potrebbe provare a indovinare il tipo mancante dal corpo della funzione, ma di proposito non deduce mai i tipi nella firma di una funzione. Le firme sono il contratto su cui si basa il resto del codice, quindi vanno scritte per intero.
Alcuni parametri hanno un valore predefinito. HashMap<K, V, S = RandomState> ha un terzo parametro per l’hasher, ed è per questo che per una HashMap rustc dice «at least 2» (almeno 2). I valori predefiniti possono riempire solo i parametri in fondo, e solo dove l’autore del tipo li ha previsti. Vedi Generici in Rust e Result in Rust.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: scrivi il tipo che manca
Sezione intitolata “Soluzione 1: scrivi il tipo che manca”Scegli questa nella maggior parte dei casi. Scopri che cosa dovrebbe essere il parametro mancante e scrivilo. Per parse(), il tipo di errore di un numero che non si riesce a leggere è std::num::ParseIntError:
use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> { text.trim().parse()}
fn main() { for input in ["42", "quaranta"] { match parse_age(input) { Ok(age) => println!("età: {}", age), Err(e) => println!("errore: {}", e), } }}Output:
età: 42errore: invalid digit found in stringIl messaggio «invalid digit found in string» (cifra non valida nella stringa) viene dalla libreria standard, quindi resta in inglese. Non sai di che tipo è l’errore? Guarda la documentazione della funzione di cui restituisci l’errore. La stessa correzione vale per HashMap<String>: di’ che cosa sono i valori, come in HashMap<String, u32>. Vedi HashMap in Rust ed Errori personalizzati in Rust.
Soluzione 2: usa l’alias a un solo tipo che intendevi, come io::Result
Sezione intitolata “Soluzione 2: usa l’alias a un solo tipo che intendevi, come io::Result”Scegli questa quando lavori con file, rete o stdin, o quando hai visto Result<T> nel codice di qualcun altro. std::io::Result<T> è l’abbreviazione di Result<T, std::io::Error>. Importa il modulo e scrivi io::Result:
use std::fs;use std::io;
fn read_config(path: &str) -> io::Result<String> { fs::read_to_string(path)}
fn main() { match read_config("missing-config.toml") { Ok(text) => println!("{}", text), Err(e) => println!("impossibile leggere la configurazione: {}", e), }}Output:
impossibile leggere la configurazione: No such file or directory (os error 2)La parte finale del messaggio viene dal sistema operativo («il file o la cartella non esiste»), quindi resta in inglese. Le app che usano il crate anyhow scrivono anyhow::Result<T> allo stesso modo. Anche lo sbaglio opposto è E0107: io::Result<String, io::Error> dà type alias takes 1 generic argument but 2 generic arguments were supplied, perché l’alias contiene già il tipo di errore. Togli il secondo. Vedi File I/O in Rust e anyhow e thiserror.
Soluzione 3: rendi generica la tua funzione, o indica i tipi
Sezione intitolata “Soluzione 3: rendi generica la tua funzione, o indica i tipi”Scegli questa quando il tipo è una tua struct generica e hai scritto solo il suo nome. rustc dice missing generics for struct `Pair` (mancano i generici per la struct Pair):
error[E0107]: missing generics for struct `Pair` --> src/main.rs:6:13 |6 | fn show(p: &Pair) { // ❌ which `K` and `V`? | ^^^^ expected 2 generic arguments |note: struct defined here, with 2 generic parameters: `K`, `V` --> src/main.rs:1:8 |1 | struct Pair<K, V> { | ^^^^ - -help: add missing generic arguments |6 | fn show(p: &Pair<K, V>) { // ❌ which `K` and `V`? | ++++++Il programma che non compila:
struct Pair<K, V> { key: K, value: V,}
fn show(p: &Pair) { // ❌ which `K` and `V`? println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "volume", value: 7 }); show(&Pair { key: "theme", value: "dark" });}L’help: scrive Pair<K, V>, ma K e V in show non esistono ancora, quindi devi anche dichiararli. Visto che show viene chiamata con due tipi diversi di Pair, rendila generica, con i vincoli che le servono per stampare:
use std::fmt::Display;
struct Pair<K, V> { key: K, value: V,}
fn show<K: Display, V: Display>(p: &Pair<K, V>) { println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "volume", value: 7 }); show(&Pair { key: "tema", value: "scuro" });}Output:
volume = 7tema = scuroSe la funzione gestisce sempre un solo tipo, indica invece i tipi: fn show(p: &Pair<&str, u32>). Lo stesso vale per impl Pair { ... }, che ha bisogno di impl<K, V> Pair<K, V> { ... }. Vedi Generici in Rust.
Soluzione 4: togli l’argomento in più
Sezione intitolata “Soluzione 4: togli l’argomento in più”Scegli questa quando rustc dice «but 2 generic arguments were supplied» e ne vuole di meno. Succede spesso con il turbofish ::<> nella chiamata di una funzione:
error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied --> src/main.rs:13:20 |13 | println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many | ^^^^^^^ ----- help: remove the unnecessary generic argument | | | expected 1 generic argument |note: function defined here, with 1 generic parameter: `T` --> src/main.rs:1:4 | 1 | fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { | ^^^^^^^ -Il programma che non compila:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many}largest ha un solo parametro, T, quindi prende un solo tipo. Scrivi largest::<i32>(...), oppure togli del tutto il turbofish, visto che Rust capisce T dall’argomento:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest(&scores)); println!("{}", largest::<f64>(&[1.5, 0.5]));}Output:
951.5Qui la riga più utile è la note:: mostra la definizione ed elenca i suoi parametri, così puoi contarli da solo.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”HashMap<String> con la sola chiave. rustc dice struct takes at least 2 generic arguments but 1 generic argument was supplied. Correzione: HashMap<String, u32>, oppure scrivi HashMap::new() e lascia che Rust deduca entrambi dal primo insert. Vedi HashMap in Rust.
Una tua struct con due parametri di tipo, a cui ne dai uno. let p: Pair<&str> = ... dà struct takes 2 generic arguments but 1 generic argument was supplied. Correzione: Pair<&str, i32>, oppure Pair<_, _> per far riempire entrambi a Rust. Option<i32, i32> è il contrario: enum takes 1 generic argument but 2 generic arguments were supplied. Se volevi due valori, usa una tupla, Option<(i32, i32)>.
Contano anche i lifetime. Una struct Excerpt<'a> scritta come Excerpt<'a, 'a> dà struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Correzione: un lifetime per ogni parametro della definizione. Vedi Lifetime in Rust. Un metodo che non è affatto generico, come .into::<String>(), dà method takes 0 generic arguments but 1 generic argument was supplied; scrivi invece let s: String = x.into(); (vedi E0283).
Una sorpresa: Vec<i32, i32> non è E0107. Vec ha un secondo parametro, instabile, per l’allocatore di memoria, quindi ottieni invece E0658 (allocator_api) e qualche altro errore. La correzione è la stessa: Vec<i32>, oppure Vec<(i32, i32)> per le coppie.
Errori correlati
Sezione intitolata “Errori correlati”- E0061: lo stesso sbaglio con gli argomenti normali, una funzione che prende N argomenti ma ne sono stati forniti M.
- E0106: manca lo specificatore di lifetime, quando un riferimento in un tipo ha bisogno di un
'a. - E0252: due righe
useimportano lo stesso nome; spesso segue un E0107 quandoResultsignificafmt::Result. - E0283: servono annotazioni di tipo, la correzione giusta quando
.into::<T>()dà E0107. - E0401: una funzione interna usa il
Tgenerico della funzione esterna. - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. fn load() -> Result<String> dà E0107. Quale tipo di ritorno lo corregge per una funzione che legge un file?
2. Perché per HashMap rustc dice at least 2 generic arguments?
3. fn show(p: &Pair) dà `missing generics for struct Pair , e show viene chiamata con tipi diversi di Pair`. Qual è la correzione?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0107 — Rust error codes index (in inglese): la spiegazione ufficiale, con esempi di argomenti generici e di lifetime in eccesso o in difetto.
- Generic Data Types — The Rust Book (in inglese): come funziona
<T>in struct comePoint<T, U>, in enum comeResult<T, E>e nei metodi. - Recoverable Errors with
Result— The Rust Book (in inglese): funzioni che restituisconoResult<String, io::Error>, e l’operatore?. std::io::Result— documentazione della libreria standard (in inglese): l’alias con un solo parametro, e perché tanto codice scriveio::Result<T>.- Generic parameters — The Rust Reference (in inglese): le regole esatte, compresi i parametri di tipo predefiniti come l’hasher di
HashMap.