Why type annotations &str make the code works?

fn main() {
    let s = String::from("aaa");
    let closure = |x| s + x;
    exec(closure);
}

fn exec<T>(func: T)
where
    T: FnOnce(&str) -> String,
{
    func("bbb");
}

The top code will error, but when modify the closure

let closure = |x| s + x;

to

let closure = |x: &str| s + x;

then, it can work.
here is the error info:

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

error: could not compile `hello` (bin "hello") due to 1 previous error

Here's a good response by @Yandros :

1 Like

The general advise I would give for such code is to – if possible[1]inline the closure into the function call that expects it; this usually results in the best possible type inference.

So you’d write

    let s = String::from("aaa");
    exec(|x| s + x);

  1. i.e. if the closure isn’t also re-used elsewhere in the function ↩︎

5 Likes

Thank for the link you share that i already understand why it error.

let f = |x| {
    let _: &i32 = x;
    x
};
{
    let scoped = 42;
    f(&scoped); // `'inferred` must "fit" into this borrow…
} // <- and thus can't span beyond this point.
f(&42) // And yet `'inferred` is used here as well => Error!

the above code, signature of f is

impl Fn(&'first_fixed i32) -> &'first_fixed i32

and then, when first call clourse f will fixed the lifetime.
so the second time call clourse f will error because the lifetime mismatched.

so in my code,
i think the add of &str make the closure's signature to be:

impl Fn(&'static str) -> &'static str

so it can match any lifetime.

and the other type like i32, the add of &i32 will not works, because &i32 is not static

fn main() {
    let s = 42;
    let closure = |x| {
        let _ = s;
        x
    };
    exec(closure);
}

fn exec<T>(func: T)
where
    T: Fn(&i32) -> &i32,
{
    func(&42);
}

Outside of an annotated context, here are some examples of effective signatures.[1]

// `for<'any> fn(x: &'any x) -> &'x i32` for one inferred lifetime `'x`
let f = |x: &i32| { x };
// Hence the error: lifetime may not live long enough

// `fn (x: &'x i32) -> &'x i32` for one inferred lifetime `'x`
let f = |x| { let _: &i32 = x; x };

It's not 'static or this wouldn't work.

    let local = 42;
    f(&local);
    f(&42);

Instead what is going on here:

    {
        let scoped = 42;
        f(&scoped);
    }
    f(&42);

is that since there is only one inferred lifetime 'x, everything you pass in has to be borrowed for the same duration 'x. 'x has to include the line with f(&42) in order for that call to be possible. But scoped can't be borrowed on that line, because it drops on the line before that.


  1. closures don't really have signatures in the same sense as functions; they have (attempted) implementations of the Fn traits ↩︎

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.