1

From the Rust programming language book I reached the chapter of lifetimes. I can't understand why is it so hard for the Rust compiler to automatically give the function arguments the shortest lifetime of the parameters passed.

Example:

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Rust will complain about lifetime annotations and I would have to write the following:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {...}

This will result in giving the shortest lifetime. If Rust could automatically know the shortest lifetime and assign it to 'a then why would I annotate the function signature manually?

In other words why doesn't the Rust compiler just choose the shortest lifetime by just looking at the parameters passed string1 and string2 and assign the shortest lifetime to 'a?

P.S. I'm assuming that you will always have equal lifetimes (like this example) or one indented in another, in which case you would want the shortest lifetime to avoid dangling references.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
ezio
  • 359
  • 2
  • 13
  • 1
    You asked this question [on reddit](https://www.reddit.com/r/rust/comments/r9gdq0/why_is_it_impossible_for_rust_to_infer_the/) and got extensive answers. What different thing do you expect to learn here? – user4815162342 Dec 07 '21 at 11:30
  • @user4815162342 i replied to them but i got no response plus i just want to help the community by asking it here – ezio Dec 07 '21 at 12:53
  • The question has been asked here in the past as well. Hopefully the answers will resolve the remaining confusions. Have fun with Rust! – user4815162342 Dec 07 '21 at 12:59
  • @user4815162342 my question is slightly diffrent, as i explained i have an assumption that you will always choose the shortest lifetime, i'll try to have fun with Rust, thanks – ezio Dec 07 '21 at 13:01
  • That is a wrong assumption to make I'm general. Just as you have an example where you want the minimal of two lifetimes, there's even more examples where that's not what you want. (e.g. a function that always returned a reference tied to `x` should not be constrained by `y`) The Rust developers have built the lifetime elision rules such that annotations are not *always* required, but should be explicitly dictated by the user when there is an ambiguity. – kmdreko Dec 07 '21 at 17:09
  • @kmdreko I think the OP's question is along the lines of: why not have lifetime elision conservatively assume shared lifetime, so that `fn foo(x: &T, y: &U) -> &V` desugars into `fn foo<'a>(x: &'a T, y: &'a U) -> &'a V` instead of the error that you get now? While that would be too strict in *some* cases, it'd be still less strict than what we have now, which *always* rejects the above as error and requires explicit lifetimes. Most of the linked questions ask a different question, why doesn't the compiler infer the lifetime from the implementation. I'm no longer sure the dup is appropriate. – user4815162342 Dec 12 '21 at 18:37
  • @user4815162342 I can agree the dup isn't quite on the mark. I think a proper answer would be that assuming a single shared lifetime only makes sense in simple examples. It would wreak havoc in more complicated cases, especially where a single type has multiple lifetimes associated to it: `fn foo<'a>(x: &'a dyn Trait<'a>, y: Struct<'a, 'a>) -> &'a str` would be wrong 99% of the time but would superficially compile. I'm sure rules *could* be made that are sensible, but they would also be more complicated and would make even more assumptions about the developer's intentions. – kmdreko Dec 12 '21 at 19:13
  • @kmdreko Maybe that'd make a good answer to the question? I don't think the dup answers really cover it. – user4815162342 Dec 12 '21 at 19:18

1 Answers1

2

I am also learning rust and just read the chapter on lifetimes so feel free to correct me.

I also had the same question as you. Why not just take the shortest lifetime? But then look at this example.

fn main() {
    let x = 1;
    let a;
    {
        let y = 2;
        a = something(&x, &y);
    }
    println!("{a}");
}

fn something<'a>(q: &'a i32, w: &'_ i32) -> &'a i32 {
    println!("{w}");
    return q;
}

In this I don't really care about the lifetime of y. In my case I just needed y to do some temp work (like printing it). What I really cared about was x. Because I was returning it.

If compiler would have assumed same lifetime ('a) for w in my function, code would not have compiled because I was using returned value a after y had been dropped.

Sid
  • 378
  • 1
  • 13