コンテンツにスキップ

Rustのエラー E0107: takes N generic arguments but M were supplied(ジェネリック引数の数の間違い)

E0107 は、<...> の中の型の数が、その型や関数が期待している数と合っていないという意味のエラーです。Result は2つ(Result<T, E>)、HashMap も2つ(HashMap<K, V>)、Option と Vec は1つを求めます。1つ足りなかったり多すぎたりすると、rustc は enum takes 2 generic arguments but 1 generic argument was supplied(列挙型はジェネリック引数を2つ取るのに、1つしか渡されていない)のように言います。先頭には「struct」「enum」「type alias」「function」「method」のどれかが付きます。とても親切なエラーの1つです。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
| +++

このエラーが出るのは、次のプログラムです。

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

コメントは「Result には型が2つ必要」という意味です(エラーになる例なので、コードはそのままにしています)。

rustc は1行目を2回指しています。

  1. Result の下の ^^^^^^:「expected 2 generic arguments」(ジェネリック引数が2つ必要)。Result は2つの型パラメータを持つ列挙型です。成功したときの値の T と、エラーの E です。
  2. u32 の下の ---:「supplied 1 generic argument」(ジェネリック引数が1つ渡された)。渡したのは T だけです。
  3. help: は、仮の E を足しています。これは型をどこに書くかのヒントで、型そのものではありません。E を本当のエラー型に置き換えます。

ジェネリック引数は、申込用紙の空欄のようなものです。Result の用紙には「うまくいったら:」と「失敗したら:」の2つの欄があります。片方しか書かないと、窓口で返されてしまいます。

Result<u32> と型を1つだけ書いているコードを読んだことがある人は、とくにこのエラーに出会いがちです。そのコードは間違っていたわけではありません。io::Result<T> や anyhow::Result<T> のような型エイリアスを使っていて、それがエラー型を埋めてくれていたのです。プレリュードのふつうの Result には、そんな省略はありません。

ジェネリックな型は、すべてのパラメータに値が入るまで、完全な型ではありません。E がわからない Result<u32, E> では、値の大きさも、エラーがどんなものかもわからないので、Rust はそれを使うコードをコンパイルできません。関数の本体から足りない型を推測することもできたはずですが、Rust は関数のシグネチャでは、わざと型を推論しません。シグネチャはほかのコードが頼りにする約束なので、省略せずに書く必要があるのです。

デフォルトを持つパラメータもあります。HashMap<K, V, S = RandomState> には、ハッシュ関数のための3つ目のパラメータがあります。だから 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
▶ 実際に試してみよう

「invalid digit found in string」(文字列に無効な数字がある)は、Rust の標準ライブラリが表示するエラーメッセージなので、英語のままです。どのエラー型かわからないときは、返しているエラーを出した関数のドキュメントを確かめましょう。HashMap<String> も同じ直し方です。HashMap<String, u32> のように、値が何なのかを書きます。HashMapと独自のエラー型を見てください。

直し方2:io::Result のような、型1つの型エイリアスを使う

Section titled “直し方2:io::Result のような、型1つの型エイリアスを使う”

ファイル、ネットワーク、標準入力を扱うとき、またはほかの人のコードで 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」(そのようなファイルやディレクトリはない)は OS のメッセージです。anyhow クレートを使うアプリでは、同じように anyhow::Result<T> と書きます。逆の間違いも E0107 です。io::Result<String, io::Error> と書くと type alias takes 1 generic argument but 2 generic arguments were supplied(型エイリアスはジェネリック引数を1つ取るのに、2つ渡されている)になります。エイリアスにはもうエラー型が入っているからです。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`?
| ++++++

note: は「構造体はここで定義されていて、ジェネリックパラメータは K と V の2つ」という意味です。エラーになるプログラムはこちらです(コメントは「どの 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> と書いていますが、show の中にはまだ K も V もないので、それも宣言する必要があります。show は2種類の 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
テーマ = ダーク
▶ 実際に試してみよう

関数が1種類しか扱わないなら、代わりに型を書きます。fn show(p: &Pair<&str, u32>) です。impl Pair { ... } も同じで、impl<K, V> Pair<K, V> { ... } と書く必要があります。ジェネリクスを見てください。

rustc が「but 2 generic arguments were supplied」(なのに2つ渡されている)と言っていて、もっと少なくしてほしいときは、これを選びます。関数呼び出しのターボフィッシュ ::<> でよく起きます。

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

help は「いらないジェネリック引数を消してください」という意味です。エラーになるプログラムはこちらです(コメントは「型が1つ多い」という意味です)。

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 の1つだけなので、受け取る型も1つです。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(構造体は少なくとも2つのジェネリック引数を取るのに、1つしか渡されていない)と言います。直し方:HashMap<String, u32> と書くか、HashMap::new() と書いて、最初の insert から Rust に両方を推論させます。HashMapを見てください。

型パラメータが2つある自分の構造体に、1つだけ渡した。 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 になります。値を2つ持ちたいなら、タプルを使って Option<(i32, i32)> と書きます。

ライフタイムも数に入る。 構造体 Excerpt<'a> を Excerpt<'a, 'a> と書くと、struct takes 1 lifetime argument but 2 lifetime arguments were supplied(構造体はライフタイム引数を1つ取るのに、2つ渡されている)になります。直し方:定義の中のパラメータ1つにつき、ライフタイムは1つです。ライフタイムを見てください。.into::<String>() のように、ジェネリックでないメソッドに型を渡すと、method takes 0 generic arguments but 1 generic argument was supplied になります。代わりに let s: String = x.into(); と書きましょう(E0283 を見てください)。

1つ意外なことがあります。Vec<i32, i32> は E0107 ではありません。Vec にはメモリアロケータのための、まだ安定していない2つ目のパラメータがあるので、E0658(英語)(allocator_api)とほかのいくつかのエラーになります。直し方は同じで、Vec<i32>、ペアなら Vec<(i32, i32)> です。

  • E0061:ふつうの引数での同じ間違い。N 個の引数を取る関数に、M 個渡した。
  • E0106:ライフタイム指定子が足りない。型の中の参照に 'a が必要なとき。
  • E0252:2つの use の行が同じ名前をインポートしている。Result が fmt::Result の意味になって、E0107 が続けて出ることがよくあります。
  • 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