Rustのエラー E0631: type mismatch in closure arguments(クロージャの引数の型)
E0631 は、クロージャの引数の数は合っているけれど、少なくとも1つの型が違う、という意味のエラーです。10回のうち9回は、違いは & 1つだけです。|x: i32| と書いたのに、イテレータは &i32 を貸してくる、というパターンです。いちばん手っ取り早い直し方は、たいてい型注釈を消して Rust に任せることです。このページでは、まずその直し方を見せてから、& がどこから来るのかを説明します。そうすれば、次からは自分で予想できるようになります。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0631]: type mismatch in closure arguments --> src/main.rs:3:43 |3 | let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ 要素は &i32 | ^^^ -------- found signature defined here | | | expected due to this | = note: expected closure signature `fn(&{integer}) -> _` found closure signature `fn(i32) -> _`note: required by a bound in `map`help: consider adjusting the signature so it borrows its argument |3 | let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); // ❌ 要素は &i32 | +「type mismatch in closure arguments」は「クロージャの引数の型が合わない」という意味です。map の下には「これが原因で、こう期待された」、クロージャの下には「見つかったシグネチャはここで定義されている」と書かれています。help は「引数を借用するようにシグネチャを調整してはどうか」と提案しています。
このエラーが出るのは、次のプログラムです。
fn main() { let prices = vec![120, 45, 300]; let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ 要素は &i32 println!("{:?}", doubled);}rustc は、このすぐあとに、もう1つ長いエラーを出します。the method `collect` exists for struct `Map<...>`, but its trait bounds were not satisfied(Map<...> に collect メソッドはあるが、トレイト境界を満たしていない、E0599)です。これはただの巻き添えです。E0631 を直せば消えます。
エラーの意味
Section titled “エラーの意味”メッセージの中心は、2行の note です。「メソッドはこれをほしがった。あなたはこう書いた」と読みましょう。
- expected
fn(&{integer}) -> _:mapは、整数への参照を渡してクロージャを呼びます。{integer}は「何かの整数型だけれど、Rust はまだどれか決めていない」、_は「戻り値の型は何でもいい」という意味です。 - found
fn(i32) -> _:あなたのクロージャは、i32を値のまま受け取ると言っています。
2つを並べてみると、違いは & だけです。help: の行が、その1文字の直し方を見せてくれています。
クロークを思い浮かべてください。コートを預けると、引換券がもらえます。.iter() も同じで、要素そのものではなく、要素の引換券(参照)を渡してきます。「コートそのものじゃないと受け取らない」と言い張るクロージャは、引換券を受け取れません。
Rust がこうする理由
Section titled “Rust がこうする理由”イテレータのメソッドは、クロージャに渡す型をきっちり決めています。そして、その型はイテレータをどう作ったかで変わります。
v.iter()は各要素を貸します:&Tv.into_iter()(自分が所有するVecに対して)は各要素を手渡します:Tfilterとfindは要素をさらに貸すので、.iter()のあとだと&&Tになります。sort_byは要素を2つ貸します:&Tと&T
引数の型を書かなければ、Rust がメソッドから型を埋めてくれるので、いつでもぴったり合います。自分で型を書くと、Rust はその言葉を信じて、チェックします。値をこっそりコピーしたり、参照を所有された値に変えたりはしません。それではコードの動きが変わってしまうからです(String なら、コピーにはメモリも時間もかかります)。イテレータの3つの作り方はイテレータのレッスンで、参照については借用で説明しています。
直し方1:型注釈を消す
Section titled “直し方1:型注釈を消す”まずはこれを試しましょう。クロージャの引数には、型を書く必要がほとんどありません。map、filter、sort_by が何を渡すか、Rust はちゃんと知っています。: i32 を消せば、コンパイルが通ります。
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// 型注釈なし:イテレータが実際に渡す型を Rust が使う let doubled: Vec<i32> = prices.iter().map(|p| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age| **age >= 18).collect(); scores.sort_by(|a, b| b.cmp(a));
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores);}実行結果:
[240, 90, 600][34, 50][99, 42, 15, 7]&i32 に対して p * 2 が使えるのは、Rust の数値の演算子が参照も受け付けるからです。filter の中の age は &&i32 なので、**age で2つの参照をたどって数値にたどり着きます。b.cmp(a) のような比較は、参照のままで使えます。
直し方2:メソッドが実際に渡す型を書く
Section titled “直し方2:メソッドが実際に渡す型を書く”長いチェーンを読みやすくしたいときなど、型を見えるように残したいときは、これを選びます。rustc の help: に従って & を足しましょう。& はパターンの側に書くこともできます。|&p| は参照を分解して、数値のコピーをそのまま取り出します(整数のような Copy 型で使えます)。
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// 本当の型を書く:.iter() なら &、filter の中なら && let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age: &&i32| **age >= 18).collect(); scores.sort_by(|a: &i32, b: &i32| b.cmp(a));
// または & パターンで数値をコピーして取り出す let tax: Vec<i32> = prices.iter().map(|&p| p / 10).collect();
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores); println!("{:?}", tax);}実行結果:
[240, 90, 600][34, 50][99, 42, 15, 7][12, 4, 30]違いに注目してください。|p: &i32| は「p は参照です」という意味で、|&p| は「参照を受け取るので、その先にあるものをください」という意味です。
直し方3:イテレータが渡すものを変える
Section titled “直し方3:イテレータが渡すものを変える”参照ではなく、本当に値そのものがほしいときは、これを選びます。方法は2つあります。
.iter().copied()は、各&i32をi32に変えます。数値、char、boolのような小さなCopy型に使います(.cloned()はStringのような型で同じことを、clone によって行います)。Vecに対する.into_iter()は、要素そのものを手渡します。そのあとVecは使えなくなるので、もう必要ないときに選びましょう。
fn main() { let prices = vec![120, 45, 300];
// .copied() で &i32 の要素を i32 の要素に変える let doubled: Vec<i32> = prices.iter().copied().map(|p: i32| p * 2).collect(); println!("{:?}", doubled);
// Vec の .into_iter() は要素そのものを手渡す(Vec は使い切られる) let names = vec![String::from("ferris"), String::from("corro")]; let loud: Vec<String> = names.into_iter().map(|name: String| name.to_uppercase()).collect(); println!("{:?}", loud);}実行結果:
[240, 90, 600]["FERRIS", "CORRO"]ループのあとでも names を使いたいなら、.iter() のままにして、&String を受け取りましょう。借りているだけの Vec から値をムーブして取り出そうとすると、別のエラー E0507 になります。
直し方4:名前付きの関数を、イテレータが渡す型に合わせる
Section titled “直し方4:名前付きの関数を、イテレータが渡す型に合わせる”.map(shout) のように関数を名前で渡しているときは、これを選びます。この場合、メッセージは「closure」ではなく「function」になります。
error[E0631]: type mismatch in function arguments --> src/main.rs:7:51 |1 | fn shout(word: &String) -> String { | --------------------------------- found signature defined here...7 | let loud: Vec<String> = words.into_iter().map(shout).collect(); | --- ^^^^^ expected due to this | | | required by a bound introduced by this call | = note: expected function signature `fn(&str) -> _` found function signature `fn(&String) -> _`「関数の引数の型が合わない」という意味で、期待されたシグネチャは fn(&str) -> _、見つかったのは fn(&String) -> _ です。
要素は &str なのに、shout は &String をほしがっています。よい習慣があります。テキストを読むだけの関数は &str を受け取るようにしましょう。そうすれば、文字列リテラルも &String も、ほかのものも受け取れます。filter のようにイテレータが & を1つ余分に付けるときは、呼び出しを短いクロージャで包みます。普通の関数呼び出しの中なら、Rust が余分な & を外してくれます(これを参照外し型強制、deref coercion と呼びます)。でも、関数を名前で渡すときにはそれができません。
// &str を受け取る:文字列リテラルも &String も受け取れるfn shout(word: &str) -> String { word.to_uppercase()}
fn is_long(word: &str) -> bool { word.len() > 4}
fn main() { let words = vec!["crab", "rustacean", "ferris"];
// 要素は &str なので、shout を名前で渡せる let loud: Vec<String> = words.iter().copied().map(shout).collect(); println!("{:?}", loud);
// filter は &&str を渡す:小さなクロージャで包めば Rust が変換してくれる let long: Vec<&str> = words.iter().copied().filter(|w| is_long(w)).collect(); println!("{:?}", long);}実行結果:
["CRAB", "RUSTACEAN", "FERRIS"]["rustacean", "ferris"](word.len() は文字数ではなくバイト数を数えるので、ここでは単語を英語のままにしています。)&str と String の違いは、文字列で詳しく説明しています。
よく出会う場面
Section titled “よく出会う場面”型を書いた引数でソートするとき。 scores.sort_by(|a: i32, b: i32| b.cmp(&a)) は E0631 になり、expected closure signature `for<'a, 'b> fn(&'a {integer}, &'b {integer}) -> _` と表示されます。for<'a, 'b> の部分は「どんな借用でも」と言っているだけです。直し方:|a, b| b.cmp(a) と書きます。ソートも見てください。
マップの要素に所有された型を書くとき。 HashMap<String, i32> に対して stock.iter().map(|(name, count): (String, i32)| ...) と書くと、expected closure signature `fn((&String, &_)) -> _` になります。マップの .iter() は、キーも値も貸してくるからです。直し方:型注釈を消すか、(&String, &i32) と書きます。
自分で書いたジェネリック関数。 fn apply<F: Fn(i32) -> i32>(f: F, x: i32) にクロージャ |x: f64| x / 2.0 を渡すと、rustc は境界を指して expected closure signature `fn(i32) -> _` と found closure signature `fn(f64) -> _` を表示します。直し方:クロージャが i32 を受け取るようにして中で変換する(x as f64 / 2.0)か、本当に f64 のつもりなら境界のほうを変えます。クロージャのトレイトも見てください。
for_each に String と書くとき。 Vec<&str> に対する words.iter().for_each(|w: String| ...) は fn(&&str) を期待されます。&str は String ではありません。直し方:|w|、または |w: &&str| と書きます。
関連するエラー
Section titled “関連するエラー”- E0593: closure takes the wrong number of arguments:引数の数についての姉妹エラーです(たとえば、タプルが1つ渡されるのに
|a, b|と書いたとき)。 - E0057: closure called with the wrong number of arguments:自分でクロージャを呼ぶときに、値が少なすぎる・多すぎる。
- E0308: mismatched types:一般的な「型が違う」エラーです。たとえば、クロージャの戻り値の型が違うときや、
fn(...)ポインタの引数に渡したときに出ます。 - E0599: no method found:E0631 のあとに出る、
collectやsumの「trait bounds were not satisfied」(トレイト境界を満たしていない)エラーです。E0631 を直せば一緒に消えます。 - E0507: cannot move out of a borrowed value:
.iter()で借りただけの要素の所有権を取ろうとしたときに出ます。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. let v = vec![1, 2, 3]; のとき、v.iter().map(|x| ...) のクロージャが受け取る型はどれですか?
2. note に expected closure signature fn(&{integer}) -> _、found closure signature fn(i32) -> _ と出ました。いちばん手っ取り早い直し方はどれですか?
3. prices.iter().map(...) の中の |&p| p * 2 は何をしていますか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0631 — Rust error codes index(英語):このエラーの公式の説明です。
- 一連の要素をイテレータで処理する — The Rust Programming Language 日本語版:
iter、into_iter、iter_mutと、それぞれが何を渡すかを説明している章です。 - クロージャ:環境をキャプチャする匿名関数 — The Rust Programming Language 日本語版:クロージャの引数の型がどう推論されるか、自分で書くのはどんなときかがわかります。
Iterator::copied— 標準ライブラリのドキュメント(英語):&TのイテレータをTのイテレータに変えるメソッドです。slice::sort_by— 標準ライブラリのドキュメント(英語):比較のクロージャが参照を2つ受け取ることが、シグネチャからわかります。