Function vs closure argument lifetime inference


#1
#[derive(Debug)]
struct MutRef<'a, T: 'a> {
    inner: &'a mut T,
}

#[inline]
fn fix<F, T>(f: F) -> F
    where F: Fn(&mut T) -> MutRef<T>
{
    f   
}    

fn main() {
    let x = |s: &mut String | -> MutRef<String> {
        MutRef { inner: s }
    };

    let fx = fix(|s: &mut String | -> MutRef<String> {
        MutRef { inner: s }
    });
    
    fn y(s: &mut String) -> MutRef<String> {
         MutRef { inner: s }
    }
    
    // doesn't compile: E0495
    // let mut s = String::from("hi");
    // println!("{:?}", x(&mut s));
    // doesn't compile on stable: E0271
    let mut t = String::from("hello");
    println!("{:?}", fx(&mut t));
    let mut u = String::from("how are you");
    println!("{:?}", y(&mut u));

}

playground link: https://play.rust-lang.org/?gist=d55fb6aaaa37787f11d5d0f01ad86555&version=nightly

I thought I knew most of how rust works at this point, but this has been stumping me. why do x and y seemingly have different inferred lifetimes? what changed recently to allow fix() to work in nightly and beta but not stable?


#2

It looks like the reborrow issue:

https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/


#3

There are known issues with how lifetimes are inferred/bound in closures - I think this is an example of that. Generally the workaround is to use functions, instead of closures, to bind lifetimes properly.

These issues are a particularly unfortunate papercut precisely because they don’t make sense and leave one with the “I thought I understood Rust but …” sentiment. Hopefully this will be resolved in the not too distant future.