Salta ai contenuti

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.

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),
}
}
}

rustc indica due volte la riga 1:

  1. Il ^^^^^^ sotto Result: «expected 2 generic arguments» (attesi 2 argomenti generici). Result è un enum con due parametri di tipo: T per il valore in caso di successo ed E per l’errore.
  2. Il --- sotto u32: «supplied 1 generic argument» (fornito 1 argomento generico). Hai dato solo T.
  3. L’help: aggiunge un segnaposto E. È un’indicazione su dove va il tipo, non il tipo stesso: sostituisci E con 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.

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.

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à: 42
errore: invalid digit found in string
▶ Provalo tu

Il 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)
▶ Provalo tu

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 = 7
tema = scuro
▶ Provalo tu

Se 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.

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:

95
1.5
▶ Provalo tu

Qui la riga più utile è la note:: mostra la definizione ed elenca i suoi parametri, così puoi contarli da solo.

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.

  • 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 use importano lo stesso nome; spesso segue un E0107 quando Result significa fmt::Result.
  • E0283: servono annotazioni di tipo, la correzione giusta quando .into::<T>() dà E0107.
  • E0401: una funzione interna usa il T generico della funzione esterna.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

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