Nika Kurashvili
Nika Kurashvili

Reputation: 6472

What does "smaller" mean for multiple references that share a lifetime specifier?

The Rust Programming Language says:

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

The function signature now tells Rust that for some lifetime 'a, the function takes two parameters, both of which are string slices that live at least as long as lifetime 'a. The function signature also tells Rust that the string slice returned from the function will live at least as long as lifetime 'a. In practice, it means that the lifetime of the reference returned by the longest function is the same as the smaller of the lifetimes of the references passed in. These relationships are what we want Rust to use when analyzing this code.

I don't get why it says:

In practice, it means that the lifetime of the reference returned by the longest function is the same as the smaller of the lifetimes of the references passed in.

Note the word "smaller". For both parameters and returned values, we specified 'a which is the same. Why does the book say "smaller"? If that was the case, we would have different specifiers(a', b').

Upvotes: 1

Views: 196

Answers (1)

mhutter
mhutter

Reputation: 2916

It's important to note that in the example, whatever is passed as x and y do not have to have identical lifetimes.

Let's rework the example to use &i32 which makes for easier demonstrations:

fn biggest<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
    if x > y {
        x
    } else {
        y
    }
}

Now, given the following example:

fn main() {
    let bigger;
    let a = 64;

    {
        let b = 32;
        bigger = biggest(&a, &b);
    }

    dbg!(bigger);
}

We have 3 different lifetimes:

  • bigger which lives until the end of main
  • a which lives until the end of main
  • b which lives until the end of its block

Now lets take the description apart

the lifetime of the reference returned by the [...] function

In our case this would be bigger, which lives until the end of main

smaller of the lifetimes of the references passed in

We passed in a, which gets dropped at the end of main, and b, which gets dropped at the end of its block. Since b gets dropped first, it is the "smaller" lifetime.

And sure enough, the compiler yells at us:

error[E0597]: `b` does not live long enough
  --> src/main.rs:7:30
   |
7  |         bigger = biggest(&a, &b);
   |                              ^^ borrowed value does not live long enough
8  |     }
   |     - `b` dropped here while still borrowed
9  |
10 |     dbg!(bigger);
   |          ------ borrow later used here

But if we move bigger inside the block, and hence reduce its lifetime, the code compiles:

fn main() {
    let a = 64;

    {
        let b = 32;
        let bigger = biggest(&a, &b);
        dbg!(bigger);
    }

    println!("This works!");
}

Now a and b still have different lifetimes but the code compiles since the the smaller of both lifetimes (b's) is the same as bigger's lifetime.

Did this help illustrate what "smaller" lifetime means?

Upvotes: 4

Related Questions