'Does Rust narrow lifetimes to satisfy constraints defined on them?

Please consider this example (playground):

struct Container<'short, 'long: 'short, T: 'long> {
    short_prop: &'short u8,
    long_prop: &'long T,
}

fn main() {
    let value = 9;
    let long = &value;

    {
        let short = &value;
        let res = Container {
            short_prop: long,
            long_prop: short, // why is this not an error?
        };

        println!("{} {}", res.short_prop, res.long_prop);
    }
}

In Container I specify 'long: 'short which I'm assuming means that 'long should live at least as long as 'short. In main, the variable long should live longer than short because it has a bigger scope.

So Container expects whatever goes into long_prop to live at least as long as whatever goes into short_prop. Yet the compiler is perfectly happy with me passing the longer-lived long into short_prop and the shorter-lived short into long_prop.

Is this because at the instantiation of Container the compiler narrows the lifetime of long to be equal to the lifetime of short thus satisfying the 'long: 'short constraint (since equality of lifetimes is fine)? Or is there another reason?



Solution 1:[1]

Is this because at the instantiation of Container the compiler narrows the lifetime of 'long to be equal to the lifetime of 'short thus satisfying the 'long: 'short constraint (since equality of lifetimes is fine)?

Yes.

Or is there another reason?

Nope.

Here's an example with a simpler Container struct which only holds 1 reference at a time. The reason it can hold both long and short references is because longer lifetimes are subtypes of shorter ones, but the overall lifetime of the container variable is narrowed to the minimum overlap of both of these lifetimes, which means container goes out of scope the same time as short does:

#[derive(Debug)]
struct Container<'a> {
    some_ref: &'a str
}

fn main() {
    let long = String::from("long");
    let long_ref = &long;
    
    let mut container = Container {
        some_ref: long_ref,
    };
    
    dbg!(&container);

    {
        let short = String::from("short");
        let short_ref = &short;

        container.some_ref = short_ref;

        dbg!(&container);
    } // both short & container go out of scope here
    
    dbg!(&container); // error, container dropped in block above, has same lifetime as short
}

playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 pretzelhammer