Unexpected lifetime warning when passing a string to a println closure

X problem: I want to keep the program functionality the same, but move all println calls into main.rs, out of the library portion of the code.

Y1 problem: I would like to pass the output string to a closure passed in from main
Y2 problem: I need to create a local closure in one function to accept the output string and replace it with a different one which I pass to the closure from main
Y3 problem: Why is rustc complaining about lifetimes, can't it infer them here like it normally does? Besides, these are the more restrictive Fn type, can't claim the argument unlike FnOnce.

The example below is a minimal example showing the same error I got to in problem Y2, but from problem Y1. Hopefully adding another closure doesn't make the solution more complicated (though I suspect it would, adding one more moving part...).

fn call_function<F>(report: F)
  where F: Fn(&str) {
    report("something")
}

fn main() {
    let closure = |thing| { println!("Did {}", &thing) };
    call_function(closure);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: implementation of `FnOnce` is not general enough
 --> src/main.rs:8:5
  |
8 |     call_function(closure);
  |     ^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: closure with signature `fn(&'2 str)` 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 `playground` due to previous error

(deleted)

The current compiler doesn't always infer a higher-ranked closure [1] when it should; see this RFC for example.

This change fixes it for your playground:

-    let closure = |thing| { println!("Did {}", &thing) };
+    let closure = |thing: &str| { println!("Did {}", thing) };

  1. one that can take any lifetime ↩ī¸Ž

4 Likes

Thank you - that did fix that problem. I'll keep in mind that I might need to specify types a bit more often.

The related problem I've been unable to search for effectively is replicated in Rust Playground.

The closure is not getting passed as a trait object, which I thought would be implicit. This is the Y2 problem. Close to solving the X problem :).

I feel like I run across a lot of odd cases which the rust book and online examples don't cover well, possibly because of my mixed experience with embedded systems (C mostly) and interpreted languages, leading me to attempt things normal Rust programmers don't. I love that it can handle it though.

It's implicit at coercion sites where the type of a variable or function parameter makes it obvious that the pointer type (here &) needs to be converted to a trait obejct pointer. But you're wrapping the & in Some before it gets passed to the function, and there is no automatic conversion for Option<&T>, so you need an explicit coercion or a different design (such as not making the callback optional).

1 Like

Thank you. I was able to cast it to &dyn Fn(&str), though I then had to fix lifetimes since I couldn't pass a reference to the closure declared in the if let scope to something outside it.

Perhaps I like nullable arguments too much.

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.