error: implementation of `FnOnce` is not general enough

呼出可能型 (Fn, FnMut, FnOnce の実装型) に HRTB が含まれ、その箇所を型推論している。

背景

HRTB - 型推論』で紹介する通り、型推論の結果に HRTB は含まれない。

これは呼出可能型に限った話ではないが、呼出可能型では専用のメッセージになる。

パターン

パターン A

クロージャ

呼出可能型にクロージャが指定され、その引数が問題になるパターン。

サンプル

以下では、関数 callback のコールバック引数 f は HRTB を含む引数をとる。
しかし、クロージャ nop の引数 _ は型推論されているため HRTB を含めない。


fn main() {
    let nop = |_| {};
    callback(nop, &42);
}

fn callback(f: impl FnOnce(&i32), x: &i32) {
    f(&x)
}

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:3:5
  |
3 |     callback(nop, &42);
  |     ^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'2 i32)` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2`

解決策

問題を起こしている推論箇所を省略しないようにする。


fn main() {
    let nop = |_: &_| {};
    callback(nop, &42);
}

fn callback(f: impl FnOnce(&i32), x: &i32) {
    f(&x)
}

パターン B

メソッド

呼出可能型にメソッドが指定され、その self 引数が問題になるパターン。

サンプル

以下では、関数 callback のコールバック引数 f は HRTB を含む引数をとる。
しかし、メソッド MyRef::workMyRef の型パラメタ 'a を HRTB として扱えない。


fn main() {
    let x = MyRef(&42);
    callback(MyRef::work, x);
}

fn callback(f: impl FnOnce(MyRef<'_>), x: MyRef<'_>) {
    f(x);
}

struct MyRef<'a>(&'a i32);

impl<'a> MyRef<'a> {
    fn work(self) {
        dbg!(self.0);
    }
}

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:3:5
  |
3 |     callback(MyRef::work, x);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: `fn(MyRef<'2>) {MyRef::<'2>::work}` must implement `FnOnce<(MyRef<'1>,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(MyRef<'2>,)>`, for some specific lifetime `'2`

解決策

対象のメソッドの呼出を適切にラップする。


fn main() {
    let x = MyRef(&42);
    callback(|x| MyRef::work(x), x);
}

fn callback(f: impl FnOnce(MyRef<'_>), x: MyRef<'_>) {
    f(x);
}

struct MyRef<'a>(&'a i32);

impl<'a> MyRef<'a> {
    fn work(self) {
        dbg!(self.0);
    }
}