Generic Fn wrapper breaks without type annotations

6d55425
Opened by hban at 2023-06-02 09:19:04

I have a generic FnWrapper struct for closures that take a &i32 as their only parameter, and a generic apply function that consumes such wrappers (not directly, but via Foo trait).

trait Foo {
    fn foo(self, value: &i32);
}

struct FnWrapper<F>(F);

impl<F: FnOnce(&i32)> Foo for FnWrapper<F> {
    fn foo(self, value: &i32) {
        (self.0)(value);
    }
}

fn apply<F: Foo>(_: F) {}

fn main() {
    // Error.
    apply(FnWrapper(|_| {}));

    // Ok with type annotations.
    apply(FnWrapper(|_: &i32| {}));
}

Run in playground.

When invoking apply without any annotations, compiler complains about:

error[E0271]: type mismatch resolving `for<'r> <[closure@<anon>:17:21: 17:27] as std::ops::FnOnce<(&'r i32,)>>::Output == ()`
  --> <anon>:17:5
   |
17 |     apply(FnWrapper(|_| {}));
   |     ^^^^^ expected bound lifetime parameter , found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#0r
   = note: required because of the requirements on the impl of `Foo` for `FnWrapper<[closure@<anon>:17:21: 17:27]>`
   = note: required by `apply`

error[E0281]: type mismatch: the type `[closure@<anon>:17:21: 17:27]` implements the trait `std::ops::FnOnce<(_,)>`, but the trait `for<'r> std::ops::FnOnce<(&'r i32,)>` is required (expected concrete lifetime, found bound lifetime parameter )
  --> <anon>:17:5
   |
17 |     apply(FnWrapper(|_| {}));
   |     ^^^^^
   |
   = note: required because of the requirements on the impl of `Foo` for `FnWrapper<[closure@<anon>:17:21: 17:27]>`
   = note: required by `apply`

Simply adding type annotation to the closure parameter will make this compile, so it looks like a bug to me.

  1. I think that if you add where F: FnOnce(&i32) to the struct definition itself, it helps.

    bluss at 2016-10-29 09:47:22

  2. It does indeed, but it looks like (another) workaround to me. Since apply function parameter has a trait bound, the constraint on just trait implementation should be enough for the compiler, I think.

    hban at 2016-10-29 09:55:49

  3. Yes, this is a major annoyance. #35714 is almost the same issue.

    bluss at 2016-10-29 10:05:37

  4. Triage: no change

    Steve Klabnik at 2020-05-07 12:29:12

  5. Current output:

    error: implementation of `FnOnce` is not general enough
      --> src/main.rs:17:5
       |
    17 |     apply(FnWrapper(|_| {}));
       |     ^^^^^^^^^^^^^^^^^^^^^^^^ 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`
    
    error[[E0308]](https://doc.rust-lang.org/nightly/error_codes/E0308.html): mismatched types
      --> src/main.rs:17:5
       |
    17 |     apply(FnWrapper(|_| {}));
       |     ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
       |
       = note: expected trait `for<'a> FnOnce<(&'a i32,)>`
                  found trait `FnOnce<(&i32,)>`
    note: this closure does not fulfill the lifetime requirements
      --> src/main.rs:17:21
       |
    17 |     apply(FnWrapper(|_| {}));
       |                     ^^^
    note: the lifetime requirement is introduced here
      --> src/main.rs:13:13
       |
    13 | fn apply<F: Foo>(_: F) {}
       |             ^^^
    help: consider specifying the type of the closure parameters
       |
    17 |     apply(FnWrapper(|_: &_| {}));
       |                     ~~~~~~~
    
    For more information about this error, try `rustc --explain E0308`.
    error: could not compile `playground` (bin "playground") due to 2 previous errors
    

    Dylan DPC at 2023-06-02 09:19:04