Перейти к содержимому

Ошибка Rust E0107: takes N generic arguments but M were supplied (число аргументов)

E0107 значит: число типов внутри <...> не совпадает с тем, что ожидает тип или функция. Result хочет два (Result<T, E>), HashMap — два (HashMap<K, V>), Option и Vec — по одному. Дашь на один меньше или на один больше — и rustc скажет что-то вроде enum takes 2 generic arguments but 1 generic argument was supplied, где в начале стоит «struct», «enum», «type alias», «function» или «method». Это одна из самых дружелюбных ошибок: rustc называет оба числа, а исправление — добавить или убрать тип. Страница входит в раздел Ошибки компилятора Rust с объяснениями.

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
| +++

По-русски: «перечисление принимает 2 обобщённых аргумента, а передан 1». help предлагает добавить недостающий аргумент.

Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «Result нужны два типа»):

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 дважды указывает на строку 1:

  1. ^^^^^^ под Result: «expected 2 generic arguments» — ожидалось 2 обобщённых аргумента. Result — это перечисление с двумя параметрами типа: T для успешного значения и E для ошибки.
  2. --- под u32: «supplied 1 generic argument» — передан 1 аргумент. Ты указал только T.
  3. help: добавляет заполнитель E. Это подсказка, где должен стоять тип, а не сам тип: E нужно заменить настоящим типом ошибки.

Обобщённые аргументы — как пустые поля в бланке. У бланка Result два поля: «если получилось:» и «если не получилось:». Заполни только одно — и тебе вернут бланк обратно.

Эта ошибка очень частая, если ты видел код, где пишут Result<u32> с одним типом. Тот код не был ошибочным: в нём использовался псевдоним типа (type alias) вроде io::Result<T> или anyhow::Result<T>, который сам подставляет тип ошибки. У обычного Result из прелюдии такого сокращения нет.

Обобщённый тип не становится полным, пока у каждого параметра нет значения. Result<u32, E> с неизвестным E не говорит, какого размера значение и как выглядит ошибка, поэтому Rust не может скомпилировать код, который его использует. Rust мог бы попытаться угадать недостающий тип по телу функции, но он намеренно никогда не выводит типы в сигнатуре функции. Сигнатура — это договор, на который опирается другой код, поэтому её нужно писать полностью.

У некоторых параметров есть значения по умолчанию. У HashMap<K, V, S = RandomState> есть третий параметр для хешера, поэтому для HashMap rustc пишет «at least 2» («как минимум 2»). Значения по умолчанию заполняются только с конца и только там, где их добавил автор типа. Подробнее — в уроках Обобщения и Result.

Это нужно чаще всего. Выясни, каким должен быть недостающий параметр, и напиши его. Для parse() тип ошибки числа, которое не удалось разобрать, — std::num::ParseIntError:

use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> {
text.trim().parse()
}
fn main() {
for input in ["42", "сорок"] {
match parse_age(input) {
Ok(age) => println!("возраст: {}", age),
Err(e) => println!("ошибка: {}", e),
}
}
}

Вывод:

возраст: 42
ошибка: invalid digit found in string
▶ Попробуй сам

Текст ошибки «invalid digit found in string» («в строке найдена недопустимая цифра») печатает сама стандартная библиотека, поэтому он на английском.

Не знаешь, какой это тип ошибки? Загляни в документацию функции, чью ошибку ты возвращаешь. То же исправление подходит для HashMap<String>: укажи, какие в нём значения, например HashMap<String, u32>. Подробнее — в уроках HashMap и Свои типы ошибок.

Исправление 2: Используй псевдоним с одним типом, который ты имел в виду, например io::Result

Заголовок раздела «Исправление 2: Используй псевдоним с одним типом, который ты имел в виду, например io::Result»

Выбирай этот вариант, когда работаешь с файлами, сетью или stdin или когда видел Result<T> в чужом коде. std::io::Result<T> — это сокращение для Result<T, std::io::Error>. Импортируй модуль и пиши 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!("не удалось прочитать конфиг: {}", e),
}
}

Вывод:

не удалось прочитать конфиг: No such file or directory (os error 2)
▶ Попробуй сам

Сообщение «No such file or directory» («нет такого файла или каталога») приходит от операционной системы.

Приложения на крейте anyhow так же пишут anyhow::Result<T>. Обратная ошибка — тоже E0107: io::Result<String, io::Error> даёт type alias takes 1 generic argument but 2 generic arguments were supplied, потому что псевдоним уже содержит тип ошибки. Убери второй тип. Подробнее — в уроках Работа с файлами и anyhow и thiserror.

Исправление 3: Сделай функцию обобщённой или назови типы

Заголовок раздела «Исправление 3: Сделай функцию обобщённой или назови типы»

Выбирай этот вариант, когда тип — твоя собственная обобщённая структура, а ты написал только её имя. rustc пишет missing generics for struct `Pair` («у структуры 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`?
| ++++++

Программа с ошибкой (комментарий значит «какие K и V?»):

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" });
}

help: пишет Pair<K, V>, но K и V в show пока не существуют, так что их нужно ещё и объявить. Раз show вызывается с двумя разными видами Pair, сделай её обобщённой, с ограничениями, которые нужны для вывода:

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: "громкость", value: 7 });
show(&Pair { key: "тема", value: "тёмная" });
}

Вывод:

громкость = 7
тема = тёмная
▶ Попробуй сам

Если функция всегда работает только с одним видом, лучше назови типы: fn show(p: &Pair<&str, u32>). То же с impl Pair { ... }: нужно писать impl<K, V> Pair<K, V> { ... }. Подробнее — в уроке Обобщения.

Выбирай этот вариант, когда rustc пишет «but 2 generic arguments were supplied» и хочет меньше. Часто это бывает с «турборыбой» (turbofish) ::<> при вызове функции:

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 {
| ^^^^^^^ -

Программа с ошибкой (комментарий значит «на один тип больше»):

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 один параметр, T, поэтому она принимает один тип. Пиши largest::<i32>(...) или вообще убери турборыбу: Rust сам поймёт T по аргументу:

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]));
}

Вывод:

95
1.5
▶ Попробуй сам

Самая полезная строка здесь — note:: она показывает определение и перечисляет его параметры, так что их можно посчитать самому.

HashMap<String> только с ключом. rustc пишет struct takes at least 2 generic arguments but 1 generic argument was supplied. Исправление: HashMap<String, u32> или HashMap::new(), чтобы Rust вывел оба типа по первому insert. Подробнее — в уроке HashMap.

Твоя структура с двумя параметрами типа, а указан один. let p: Pair<&str> = ... даёт struct takes 2 generic arguments but 1 generic argument was supplied. Исправление: Pair<&str, i32> или Pair<_, _>, чтобы Rust заполнил оба сам. Option<i32, i32> — обратный случай: enum takes 1 generic argument but 2 generic arguments were supplied. Если нужны два значения, используй кортеж: Option<(i32, i32)>.

Времена жизни тоже считаются. Структура Excerpt<'a>, записанная как Excerpt<'a, 'a>, даёт struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Исправление: по одному времени жизни на каждый параметр из определения. Подробнее — в уроке Времена жизни. Метод, который вообще не обобщённый, например .into::<String>(), даёт method takes 0 generic arguments but 1 generic argument was supplied; вместо этого пиши let s: String = x.into(); (см. E0283).

Один сюрприз: Vec<i32, i32> — не E0107. У Vec есть второй, нестабильный параметр для аллокатора памяти, поэтому вместо неё получишь E0658 (allocator_api, на английском) и ещё несколько ошибок. Исправление то же: Vec<i32> или Vec<(i32, i32)> для пар.

  • E0061: та же ошибка с обычными аргументами — функция принимает N аргументов, а передано M.
  • E0106: не указано время жизни, когда ссылке в типе нужен 'a.
  • E0252: две строки use импортируют одно имя; за ней часто следует E0107, когда Result значит fmt::Result.
  • E0283: нужны аннотации типов — правильное исправление, когда .into::<T>() даёт E0107.
  • E0401: внутренняя функция использует обобщённый T внешней.
  • Все ошибки компилятора: полный список страниц об ошибках.

Проверь себя

1. fn load() -> Result<String> даёт E0107. Какой тип возврата исправит это для функции, которая читает файл?

2. Почему для HashMap rustc пишет at least 2 generic arguments?

3. fn show(p: &Pair) даёт `missing generics for struct Pair , а show вызывается с разными видами Pair`. Как исправить?

Результат: 0 / 3