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), } }}这个错误是什么意思
Section titled “这个错误是什么意思”rustc 在第 1 行标了两处:
Result下面的^^^^^^:“expected 2 generic arguments”(需要 2 个泛型参数)。Result是一个有两个类型参数的枚举:T表示成功时的值,E表示错误。u32下面的---:“supplied 1 generic argument”(只给了 1 个泛型参数)。你只给了T。help:加上了一个占位的E。它提示的是类型应该写在哪里,而不是类型本身:你要把E换成真正的错误类型。
泛型参数就像表格上的空格。Result 这张表有两个空:“成功时:”和“失败时:”。只填一个,窗口的工作人员就会把表退给你。
如果你读过写着 Result<u32>(只有一个类型)的代码,就很容易犯这个错误。那些代码并没有写错:它们用的是 io::Result<T> 或 anyhow::Result<T> 这样的类型别名,别名已经替你填好了错误类型。prelude 里的普通 Result 没有这种简写。
Rust 为什么要这样做
Section titled “Rust 为什么要这样做”泛型类型要等每个参数都有了值,才算一个完整的类型。E 未知的 Result<u32, E> 既说不清这个值有多大,也说不清错误长什么样,所以 Rust 没法编译用到它的代码。Rust 本可以试着从函数体里猜出缺少的类型,但它有意在函数签名里从不推断类型。签名是其他代码所依赖的约定,所以必须完整写出来。
有些参数确实有默认值。HashMap<K, V, S = RandomState> 有第三个参数,用来指定哈希器,所以对 HashMap rustc 会说“at least 2”(至少 2 个)。默认值只能从末尾开始补,而且只有类型的作者加了默认值的地方才行。参见泛型和 Result。
修复 1:写出缺少的类型
Section titled “修复 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", "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> { ... }。参见泛型。
修复 4:删掉多余的参数
Section titled “修复 4:删掉多余的参数”当 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]));}输出:
951.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
- E0107 — Rust error codes index(英文):官方解释,附有泛型参数和生命周期参数太多、太少的示例。
- 泛型数据类型 — Rust 程序设计语言:
<T>在Point<T, U>这样的结构体、Result<T, E>这样的枚举以及方法里怎么用。 - 用
Result处理可恢复的错误 — Rust 程序设计语言:返回Result<String, io::Error>的函数,以及?运算符。 std::io::Result— 标准库文档(英文):这个单参数别名,以及为什么那么多代码写io::Result<T>。- Generic parameters — The Rust Reference(英文):准确的规则,包括
HashMap的哈希器这样的默认类型参数。