Misleading error message when passing a reference to an FnOnce and a non-reference is expected

3ff54ed
Opened by Daniel Wagner-Hall at 2020-06-11 18:01:14

Compiling this code:

struct Foo {}

impl Foo {
    pub fn foo(&self) -> String {
        "foo".to_owned()
    }
}

fn main() {
    let foo = Foo{};
    let closure = move || foo;

    call(&closure);
}

fn call<F: FnOnce() -> Foo>(f: F) {
    println!("{}", f().foo())
}

gives me the following error:

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
  --> src/main.rs:11:19
   |
11 |     let closure = move || foo;
   |                   ^^^^^^^^^^^
12 | 
13 |     call(&closure);
   |     ---- the requirement to implement `Fn` derives from here
   |
note: closure is `FnOnce` because it moves the variable `foo` out of its environment
  --> src/main.rs:11:27
   |
11 |     let closure = move || foo;
   |                           ^^^

call does expect a closure which implements FnOnce, not one which implements Fn as the error message suggests. Accordingly, the line "expected a closure that implements the Fn trait, but this closure only implements FnOnce" is very misleading.

The actual problem here is that a reference to an FnOnce is being passed, rather than the FnOnce itself. It would be great if the error message reflected this fact.

  1. Current output, still incorrect:

    error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
      --> src/main.rs:11:19
       |
    11 |     let closure = move || foo;
       |                   ^^^^^^^^---
       |                   |       |
       |                   |       closure is `FnOnce` because it moves the variable `foo` out of its environment
       |                   this closure implements `FnOnce`, not `Fn`
    12 | 
    13 |     call(&closure);
       |     ---- the requirement to implement `Fn` derives from here
    

    Esteban Kuber at 2019-05-23 00:17:05