コンテンツにスキップ

Rustのエラー E0631: type mismatch in closure arguments(クロージャの引数の型)

E0631 は、クロージャの引数の数は合っているけれど、少なくとも1つの型が違う、という意味のエラーです。10回のうち9回は、違いは & 1つだけです。|x: i32| と書いたのに、イテレータは &i32 を貸してくる、というパターンです。いちばん手っ取り早い直し方は、たいてい型注釈を消して Rust に任せることです。このページでは、まずその直し方を見せてから、& がどこから来るのかを説明します。そうすれば、次からは自分で予想できるようになります。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

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 を直せば消えます。

メッセージの中心は、2行の note です。「メソッドはこれをほしがった。あなたはこう書いた」と読みましょう。

  • expected fn(&{integer}) -> _:map は、整数への参照を渡してクロージャを呼びます。{integer} は「何かの整数型だけれど、Rust はまだどれか決めていない」、_ は「戻り値の型は何でもいい」という意味です。
  • found fn(i32) -> _:あなたのクロージャは、i32 を値のまま受け取ると言っています。

2つを並べてみると、違いは & だけです。help: の行が、その1文字の直し方を見せてくれています。

クロークを思い浮かべてください。コートを預けると、引換券がもらえます。.iter() も同じで、要素そのものではなく、要素の引換券(参照)を渡してきます。「コートそのものじゃないと受け取らない」と言い張るクロージャは、引換券を受け取れません。

イテレータのメソッドは、クロージャに渡す型をきっちり決めています。そして、その型はイテレータをどう作ったかで変わります。

  • v.iter() は各要素を貸します:&T
  • v.into_iter()(自分が所有する Vec に対して)は各要素を手渡します:T
  • filter と find は要素をさらに貸すので、.iter() のあとだと &&T になります。
  • sort_by は要素を2つ貸します:&T と &T

引数の型を書かなければ、Rust がメソッドから型を埋めてくれるので、いつでもぴったり合います。自分で型を書くと、Rust はその言葉を信じて、チェックします。値をこっそりコピーしたり、参照を所有された値に変えたりはしません。それではコードの動きが変わってしまうからです(String なら、コピーにはメモリも時間もかかります)。イテレータの3つの作り方はイテレータのレッスンで、参照については借用で説明しています。

まずはこれを試しましょう。クロージャの引数には、型を書く必要がほとんどありません。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 の違いは、文字列で詳しく説明しています。

型を書いた引数でソートするとき。 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| と書きます。

理解度チェック

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