genericsrustclosurestraitslifetime

What's the difference between `T: Trait + 'a` and `impl Trait + 'a` in Rust?


I originally thought T: 'a will restrict you form using T instance outside 'a because this exmaple reports an error:

fn main() {
    let num;
    {
        let num1 = 10; // error: num1 doesn't live long enough
        num = test(&num1);
    }
    println!("num: {}", num);
}

fn test<'a, T: Copy + Display>(para: &'a T) -> impl Copy + Display + 'a {
    *para
}

However, if you change the example to:

fn main() {
    let num;
    {
        let num1 = 10;
        num = test(&num1);
    }
    println!("num: {}", num);
}

fn test<'a, T: Copy + Display + 'a>(para: &'a T) -> T {
    *para
}

Everythig is OK... So what's the difference?

PS: The idea that T: 'a will restrict you form using T instance outside 'a comes from this example:

fn say_some<'a>(name: String) -> impl Fn(&'a str) + 'a {
    move |text| println!("{name} syas: {text}")
}

fn main() {
    let func = say_some("Blackbird".into());
    {
        let string: String = "Talk is cheap.".into();
        let r = &string; // string doesn't live long enough
        func(r);
    }
}

This could be solved by changing impl Fn(&'a str) + 'a to impl Fn(&'a str) + 'static.


Solution

  • impl Trait in return type position returns an opaque type determined by the called function. The only assumptions the caller can make about the returned type are the type constraints given in the function signature – in this case that the type is Copy and Display, and that it lives for at least as long as the reference para that was passed in. Since the constraints are the only assumptions the caller is allowed to make about the return type, it can't assume that the return type lives longer than 'a.

    Type parameters, on the other hand, are determined by the caller. The constraints are the minimum the type must fulfil, but it is allowed to overfulfil the constraints. In your example, T is i32, which has a static lifetime. Since the return type is explicit and not opaque, the caller doesn't have to make any assumptions about T. It knows that T is i32, and it knows it has a static lifetime, so it can make use of that knowledge.