エラーの内容
Rustプロジェクトをコンパイルすると、ビルドが突然止まります:
error[E0282]: type annotations needed
--> src/main.rs:4:9
|
4 | let x = [].into_iter().collect();
| ^ cannot infer type for type parameter `T`
|
= note: cannot satisfy `_: Iterator`
rustcは型を自動的に推論しようと努めます。しかし、Tが何であるべきかを確定するための情報が下流に存在しない場合、コンパイラは処理を止め、明示的な指定を求めます。
なぜこのエラーが発生するのか
Rustの型推論はコード内を双方向に流れます。ジェネリック関数やトレイトメソッドがTを返すとき、その値をどう使うかで型が絞り込めない場合にこの問題が発生します。コンパイラは推測できないため、具体的な型の指定を要求します。
よくある原因:
- 型付き変数に格納せずに
.collect()を呼び出している - ターゲット型なしで文字列に
.parse()を呼び出している - 型ヒントなしで
Default::default()やFromStr::from_str()を使用している vec![]や[]などの空リテラル — 要素がないため型を推論できない- 戻り値の型が型パラメータのみに依存するジェネリック関数
修正手順
1. 変数バインディングに型を注釈する
letバインディングに直接型を追加します。ほとんどの場合、これだけで解決します。
// 修正前 — コンパイラがコレクションの型を推論できない
let numbers = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();
// 修正後 — コレクションの型を指定する
let numbers: Vec<i32> = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();
2. ターボフィッシュ構文を使う
バインディングへの型注釈を省略できます。結果を最初に変数に格納せず別の関数に直接渡す場合に便利で、ターボフィッシュ(::<T>)を使って呼び出し箇所に型を付けます。
// parse() は何にパースするかを知る必要がある
let n = "42".parse::<i32>().unwrap();
// collect() はターゲットのコレクションを知る必要がある
let v = (0..5).map(|x| x * 2).collect::<Vec<_>>();
// IPアドレスのパース
let addr = "127.0.0.1".parse::<std::net::IpAddr>().unwrap();
3. 空のコレクションリテラルを修正する
空のVec::new()はコンパイラに何のヒントも与えません。以下の3つのアプローチから選んでください:
// エラー: 要素の型を推論できる要素がない
let mut items = Vec::new();
// 方法1: バインディングに型を注釈する
let mut items: Vec<String> = Vec::new();
// 方法2: コンストラクタにターボフィッシュを使う
let mut items = Vec::<String>::new();
// 方法3: 先に要素をプッシュする(コンパイラが T = String と推論する)
let mut items = Vec::new();
items.push(String::from("hello"));
4. Default::default() を修正する
// エラー — どの型にすべきか?
let config = Default::default();
// 修正: バインディングに型を注釈する
let config: MyConfig = Default::default();
// 標準型でも同様に動作する
let counts: HashMap<String, i32> = Default::default();
5. ジェネリック関数の呼び出しを修正する
ジェネリック関数の戻り値の型が結果の使われ方から推論できない場合、呼び出し箇所で明示的に指定します:
fn make_pair<A, B>(a: A, b: B) -> (A, B) {
(a, b)
}
// リテラルに型サフィックスを付ける
let pair = make_pair(1i32, 2i32);
// または呼び出し全体にターボフィッシュを使う
let pair = make_pair::<i32, f64>(1, 2.0);
6. collect() チェーンを修正する
collect()はE0282の最大の原因です。数十種類のコレクション型を生成できるため、コンパイラはどれを選ぶか決められません。チェーンのどこかで必ず出力型を指定してください:
use std::collections::{HashMap, HashSet};
// HashSet に収集する
let unique: HashSet<i32> = vec![1, 2, 2, 3].into_iter().collect();
// キーと値のペアから HashMap に収集する
let map: HashMap<_, _> = vec![("a", 1), ("b", 2)].into_iter().collect();
// または collect に直接ターボフィッシュを使う
let unique = vec![1, 2, 2, 3].into_iter().collect::<HashSet<_>>();
プレースホルダー_はとても便利です — 外側の型を指定すれば、残りはコンパイラが補完してくれます。
修正の確認
フルビルドではなくcargo checkを実行してください — バイナリを生成しないため高速です:
cargo check
問題なければ、以下のような出力が表示されます:
Checking myproject v0.1.0 (/path/to/myproject)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
まだE0282が表示される場合は、grepを使って残っている箇所をすべて特定してください:
cargo build 2>&1 | grep E0282
クイックリファレンス
- 変数への型注釈:
let x: Vec<i32> = ... - ターボフィッシュ:
method::<Type>()またはType::<Param>::method() - 部分推論: コンパイラがまだ推論できる部分には
_を使う —Vec<_>、HashMap<String, _> - エラーID: 常に
error[E0282]—rustc --explain E0282を実行するとコンパイラの詳細な説明が表示される
型注釈が冗長になる場合
長い型注釈があちこちに散らばると、コードが読みにくくなります。型エイリアスを使うとすっきりします:
type WordCount = HashMap<String, usize>;
let counts: WordCount = text.split_whitespace()
.map(|w| (w.to_string(), 1))
.collect();
エイリアスを一つ定義し、バインディングに一度だけ注釈を付ければ — 呼び出し箇所はすっきりしたまま、コンパイラも必要なヒントを得られます。

