error: implementation of `FnOnce` is not general enough

呼出可能型 (Fn, FnMut, FnOnce の実装型) の引数に汎用性が足りない。

背景情報

呼出可能型の指定において、HRTB に対応すべき箇所がそうでないと、このエラーになる。
(注: ライフタイム省略により for<...> 構文を使わない HRTB もありうる)。

パターン

パターン A

クロージャ

呼出可能型にクロージャを指定しているパターン。クロージャの引数型を省略した場合、型推論は HRTB を候補にしない。そのため、このエラーの一因になる事がある。

サンプル

以下では、関数 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

メソッド

呼出可能型にメソッドを指定しているパターン。メソッドは引数だけでなく型そのものが持つライフタイムがあり、それは HRTB として扱えない。

サンプル

以下では、関数 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);
    }
}