The following code (MRE'ized from my actual code) fails to compile with E0597 "uni does not live long enough", but compiles fine if I add a &_ type annotation to the closure. What is going on? Some implicit HRTB shenanigans? Based on E0597 it would never ever have occurred to me to try the solution, luckily I found it more or less by accident. (Playground)
trait Shader<Uni> {
fn shade(_: Uni);
}
struct Context<Sh> {
shader: Sh,
}
impl<U, F: Fn(U)> Shader<U> for F {
fn shade(_: U) {}
}
fn render<'u, Uni, Sh>(_u: &'u Uni, _ctx: &mut Context<Sh>)
where
Sh: Shader<&'u Uni>,
{
}
fn main() {
let mut ctx = Context { shader: |_| {} }; // FAILS
//let mut ctx = Context { shader: |_: &_| {} }; // WORKS
for _ in 1..1 {
let uni = 42;
render(&uni, &mut ctx);
// ^^^^ -------- borrow later used here
// |
// borrowed value does not live long enough
}
// - `uni` dropped here while still borrowed
}
An observation to make things even more confusing:
If I change the failing line to
let mut ctx = Context { shader: |_: ()| {} };
Then the error that yields makes clear that the compiler can infer that the type of the parameter to the closure is supposed to be &{integer} (as opposed to e.g. {integer}).
With closures and type inference... it tends to be the case that |_| can only infer a reference type with a single fixed lifetime, whereas |_: &_| makes the closure generic over the reference's lifetime. At least unless the compiler already got a sufficiently “visible” hint as to what type the closure must be at the point it was defined.[1] I couldn't tell you why thats the case, but it's a useful thing to know.
E. g. something like a Fn trait bound with a generic lifetime (i. e. a HRTB) directly on the function the closure is passed to ↩︎
In other words, |_| { ... } is inferred to be |_: &'some_specific_lifetime _| {...}, while |_: &_| { ... } - to be (pseudo-Rust) for <'a> |_: &'a _| { ... }.