Confusion re returning a closure

#1

I have a need in a Rust application for returning a closure from a function. So I’ve been doing some experimenting and I’ve found something that puzzles me. The following little example works:

fn main()
{
    let hello = get_hello();
    hello();
}

fn get_hello() -> impl Fn()
{
    || {
        println!("hello world");
    }
}

But “The Book” tells me that impl Fn() is “syntax sugar” for a trait bound, like so:

fn main()
{
    let hello = get_hello();
    hello();
}

fn get_hello<F:Fn()>() -> F
{
    || {
        println!("hello world");
    }
}

But compiling this produces

Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed
 --> src/main.rs:5:5
  |
4 |     let hello = get_hello();
  |         ----- consider giving `hello` a type
5 |     hello();
  |     ^^^^^^^ cannot infer type
  |
  = note: type must be known at this point

error[E0308]: mismatched types
  --> src/main.rs:10:5
   |
8  |   fn get_hello<F:Fn()>() -> F
   |                             - expected `F` because of return type
9  |   {
10 | /     || {
11 | |         println!("hello world");
12 | |     }
   | |_____^ expected type parameter, found closure
   |
   = note: expected type `F`
              found type `[closure@src/main.rs:10:5: 12:6]`

error: aborting due to 2 previous errors

Some errors occurred: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

“The Book” suggests that these two examples should be equivalent, but clearly they are not.
Perhaps I’ve missed something. Can someone please explain what’s going on here?

Thanks –
/Don

#2
fn get_hello<F:Fn()>() -> F
{
    || {
        println!("hello world");
    }
}

This means caller specifies some F and you return it, but that’s not what happens here - you’re returning your own impl of Fn(). This is precisely what impl Fn() in the return position says, and why the first example works.

As for the Book, you’re probably referring to where it suggests that the following are equivalent:

fn get_hello(f: impl Fn()) {}
fn get_hello<F: Fn()>(f: F) {}

But to reiterate, this has nothing to do with the return position.

1 Like
#3

It lied. impl Trait is only “syntax sugar” when it’s used in argument position; it means the complete opposite when used in return type position and does not have any alternate form.

#4

Thanks, as usual.

What I missed is that the equivalence I cited is in the “Traits as Arguments” section. There’s another section on “Returning Traits”, which discusses using ‘impl Trait’ in the return position.

/Don

1 Like
#5

You wrote “It lied”. Actually not. As I said in my other post just now, the syntactic sugar is in a section that applies to using traits in arguments. There’s another section on traits in the return position. So I lifted something from the former section and applied it to the latter, incorrectly.

I do think that the Book could be more explicit, give more emphasis, to the issue of trait bounds and where clauses applying only to arguments. But it does convey this information by its (the Book’s) structure.

#6

Well, if you want to be generous, you can say it lied misled by omission. It didn’t qualify the statement about syntax sugar as only applying to impl Trait in argument position. Plus, having the same syntax mean diametrically opposing things in almost identical semantic circumstances is very unusual, and something you might expect to be explicitly called out.

Edit: actually, “lied” implies intent, which is absolutely not the case.

1 Like
#7

Calling me a liar is not very “generous.”

1 Like
#8

I’m sorry.

As I noted above, I decided “lied” wasn’t the right word because I didn’t want to imply intent to deceive. The initial “It lied” was meant to be said in a dramatic voice.

#9

For the sake of future reference, let’s also note that <T : Trait> arg: T is not exactly equivalent to arg: impl Trait although they are indeed very close. So it’s not “just sugar” :wink: