跳转到内容

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(这个枚举需要 2 个泛型参数,却只给了 1 个)这样的话,开头可能是“struct”、“enum”、“type alias”、“function”或“method”。这是最友好的错误之一:rustc 把两个数字都告诉你了,修复方法就是补上或删掉一个类型。本页属于编译错误指南。

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

产生这个错误的程序是这样的:

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>(只有一个类型)的代码,就很容易犯这个错误。那些代码并没有写错:它们用的是 io::Result<T> 或 anyhow::Result<T> 这样的类型别名,别名已经替你填好了错误类型。prelude 里的普通 Result 没有这种简写。

泛型类型要等每个参数都有了值,才算一个完整的类型。E 未知的 Result<u32, 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", "forty"] {
match parse_age(input) {
Ok(age) => println!("年龄:{}", age),
Err(e) => println!("出错:{}", e),
}
}
}

输出:

年龄:42
出错:invalid digit found in string
▶ 动手试一试

不确定是哪种错误类型?去查一下你返回的那个错误来自哪个函数,看它的文档。HashMap<String> 也是同样的修法:写明值的类型,比如 HashMap<String, u32>。参见哈希 map和自定义错误。

修复 2:用你想要的单参数别名,比如 io::Result

Section titled “修复 2:用你想要的单参数别名,比如 io::Result”

当你在处理文件、网络或标准输入,或者在别人的代码里看到过 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)
▶ 动手试一试

使用 anyhow crate 的程序也是这样写 anyhow::Result<T>。反过来的错误同样是 E0107:io::Result<String, io::Error> 会报 type alias takes 1 generic argument but 2 generic arguments were supplied(这个类型别名只需要 1 个泛型参数,却给了 2 个),因为别名里已经包含了错误类型。删掉第二个就行。参见文件读写和 anyhow 和 thiserror。

修复 3:让函数变成泛型,或者写明类型

Section titled “修复 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`?
| ++++++

出错的程序:

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>,但 show 里还不存在 K 和 V,所以你还得声明它们。因为调用 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
主题 = 深色
▶ 动手试一试

如果这个函数只处理一种 Pair,就直接写明类型:fn show(p: &Pair<&str, u32>)。impl Pair { ... } 也一样,需要写成 impl<K, V> Pair<K, V> { ... }。参见泛型。

当 rustc 说“but 2 generic arguments were supplied”(却给了 2 个),希望你少给一些时,选这个方法。它常常出现在函数调用的 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>(...),或者干脆不写 turbofish,因为 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(这个结构体至少需要 2 个泛型参数,却只给了 1 个)。修复:写成 HashMap<String, u32>,或者写 HashMap::new(),让 Rust 从第一次 insert 推断出两个类型。参见哈希 map。

自己的结构体有两个类型参数,却只给了一个。 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(需要 1 个生命周期参数,却给了 2 个)。修复:定义里有几个参数,就给几个生命周期。参见生命周期。完全不是泛型的方法,比如 .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 导入了同一个名字;当 Result 指的是 fmt::Result 时,后面往往跟着一个 E0107。
  • E0283:需要类型注解,.into::<T>() 报 E0107 时正确的修法就在那里。
  • E0401:内部函数用了外层函数的泛型 T。
  • 所有编译错误:错误页面的完整列表。

小测验

1. fn load() -> Result<String> 报了 E0107。对一个读取文件的函数来说,哪个返回类型能修好它?

2. 为什么 rustc 对 HashMap 说的是 at least 2 generic arguments?

3. fn show(p: &Pair) 报了 `missing generics for struct Pair ,而调用 show 时传入了不同种类的 Pair`。怎么修?

得分: 0 / 3