error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`

クロージャが期待されるトレイトを実装していなかった。

概要

クロージャ用のトレイトには FnOnce, FnMut, Fn の三種類があり、後に行くほど要件が追加されていく。そして、クロージャが期待されたトレイトを実装しないとエラーになる。

パターン

パターン A

Fn への不適合

キャプチャした変数の編集があると、Fn に適合しなくなる。

サンプル

以下では、クロージャがキャプチャ変数 val を編集している。


fn main() {
    let mut val = 0;
    let closure = || { val += 1;};
    callback(closure);
}

fn callback<F: Fn()>(f: F) {
    f();
}

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
 --> src\main.rs:3:19
  |
3 |     let closure = || { val += 1;};
  |                   ^^   --- closure is `FnMut` because it mutates the variable `val` here
  |                   |
  |                   this closure implements `FnMut`, not `Fn`
4 |     callback(closure);
  |     -------- ------- the requirement to implement `Fn` derives from here
  |     |
  |     required by a bound introduced by this call
  |
note: required by a bound in `callback`
 --> src\main.rs:7:16
  |
7 | fn callback<F: Fn()>(f: F) {
  |                ^^^^ required by this bound in `callback`

パターン B1

FnFnMut への不適合 (キャプチャした変数のムーブ)

キャプチャした変数をムーブすると、FnFnMut に適合しなくなる。

備考

ムーブはトレイト Copy を実装している型では発生しない。

そのため、同じようなコードでも、このエラーが発生するかはキャプチャ変数次第である。

サンプル

以下では、クロージャがキャプチャした変数 val をムーブしている。


fn main() {
    let val = Val();
	let closure = || { move_arg(val); };
    callback(closure);
}

fn move_arg(arg: Val) {
    _ = arg;
}

fn callback<F: Fn()>(f: F) {
    f();
}

struct Val();

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
  --> src\main.rs:3:19
   |
3  |     let closure = || { move_arg(val); };
   |                   ^^            --- closure is `FnOnce` because it moves the variable `val` out of its environment
   |                   |
   |                   this closure implements `FnOnce`, not `Fn`
4  |     callback(closure);
   |     -------- ------- the requirement to implement `Fn` derives from here
   |     |
   |     required by a bound introduced by this call
   |
note: required by a bound in `callback`
  --> src\lib.rs:11:16
   |
11 | fn callback<F: Fn()>(f: F) {
   |                ^^^^ required by this bound in `callback`

パターン B2

FnFnMut への不適合 (キャプチャした可変参照のムーブ)

これは前のパターンの可変参照版である。

備考

見落としがちだが、可変参照型もトレイト Copy を実装していない。

そのため、キャプチャした可変参照を他の関数などへ渡すとエラーになる。

サンプル

以下では、クロージャが可変参照 val_mut をムーブしている。


fn main() {
    let mut val = 0;
    let val_mut = &mut val;
    let closure = || { with_mut(val_mut); };
    callback(closure);
}

fn with_mut(arg: &mut i32) {
    _ = arg;
}

fn callback<F: Fn()>(f: F) {
    f();
}

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
  --> src\main.rs:4:19
   |
4  |     let closure = || { with_mut(val_mut); };
   |                   ^^            ------- closure is `FnMut` because it mutates the variable `*val_mut` here
   |                   |
   |                   this closure implements `FnMut`, not `Fn`
5  |     callback(closure);
   |     -------- ------- the requirement to implement `Fn` derives from here
   |     |
   |     required by a bound introduced by this call
   |
note: required by a bound in `callback`
  --> src\main.rs:12:16
   |
12 | fn callback<F: Fn()>(f: F) {
   |                ^^^^ required by this bound in `callback`