Ошибка 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:
^^^^^^подResult: «expected 2 generic arguments» — ожидалось 2 обобщённых аргумента.Result— это перечисление с двумя параметрами типа:Tдля успешного значения иEдля ошибки.---подu32: «supplied 1 generic argument» — передан 1 аргумент. Ты указал толькоT.help:добавляет заполнительE. Это подсказка, где должен стоять тип, а не сам тип:Eнужно заменить настоящим типом ошибки.
Обобщённые аргументы — как пустые поля в бланке. У бланка Result два поля: «если получилось:» и «если не получилось:». Заполни только одно — и тебе вернут бланк обратно.
Эта ошибка очень частая, если ты видел код, где пишут Result<u32> с одним типом. Тот код не был ошибочным: в нём использовался псевдоним типа (type alias) вроде io::Result<T> или anyhow::Result<T>, который сам подставляет тип ошибки. У обычного Result из прелюдии такого сокращения нет.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Обобщённый тип не становится полным, пока у каждого параметра нет значения. Result<u32, E> с неизвестным E не говорит, какого размера значение и как выглядит ошибка, поэтому Rust не может скомпилировать код, который его использует. Rust мог бы попытаться угадать недостающий тип по телу функции, но он намеренно никогда не выводит типы в сигнатуре функции. Сигнатура — это договор, на который опирается другой код, поэтому её нужно писать полностью.
У некоторых параметров есть значения по умолчанию. У HashMap<K, V, S = RandomState> есть третий параметр для хешера, поэтому для HashMap rustc пишет «at least 2» («как минимум 2»). Значения по умолчанию заполняются только с конца и только там, где их добавил автор типа. Подробнее — в уроках Обобщения и Result.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: Напиши недостающий тип
Заголовок раздела «Исправление 1: Напиши недостающий тип»Это нужно чаще всего. Выясни, каким должен быть недостающий параметр, и напиши его. Для 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> { ... }. Подробнее — в уроке Обобщения.
Исправление 4: Убери лишний аргумент
Заголовок раздела «Исправление 4: Убери лишний аргумент»Выбирай этот вариант, когда 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]));}Вывод:
951.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
Что почитать
Заголовок раздела «Что почитать»- E0107 — Rust error codes index (на английском): официальное объяснение с примерами лишних и недостающих обобщённых аргументов и времён жизни.
- Обобщённые типы данных — книга «Язык программирования Rust» (перевод на русский): как
<T>работает в структурах вродеPoint<T, U>, перечислениях вродеResult<T, E>и методах. - Проброс ошибок — книга «Язык программирования Rust» (перевод на русский): функции, возвращающие
Result<String, io::Error>, и оператор?. std::io::Result— документация стандартной библиотеки (на английском): псевдоним с одним параметром и почему так много кода пишетio::Result<T>.- Generic parameters — The Rust Reference (на английском): точные правила, включая параметры типа по умолчанию вроде хешера у
HashMap.