简体   繁体   中英

rust lifetimes and borrow checker

I'm reading through the "Learn rust" tutorial, and I'm trying to understand lifetimes. Chapter 10-3 has the following non-working example:

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

The paragraph below explains the error as:

When we’re defining this function, we don’t know the concrete values that will be passed into this function, so we don’t know whether the if case or the else case will execute.

However, if I change the block of code to do something else; say, print x and return y; so that we know what is being returned each time, the same error occurs. Why?

fn longest(x: &str, y: &str) -> &str {
  println!("{}", x);
  y
}

It book also says:

The borrow checker can’t determine this either, because it doesn’t know how the lifetimes of x and y relate to the lifetime of the return value. 

My doubts are:

  1. Is the borrow checker capable of tracking lifetimes across functions? If so, can you please provide an example?
  2. I don't understand the error. x and y are references passed into longest , so the compiler should know that its owner is elsewhere(and that its lifetime would continue beyond longest ). When the compiler sees that the return values are either x or y, why is there a confusion on lifetimes?

Think of the function as a black box. Neither you, nor the compiler knows what happens inside. You may say that the compiler "knows", but that's not really true. Imagine that it returns X or Y based on the result of a remote HTTP call. How can it know in advance?

Yet it needs to provide some guarantee that the returned reference is safe to use. That works by forcing you (ie the developer) to explicitly specify the relationships between the input parameters and the returned value.

First you need to specify the lifetimes of the parameters. I'll use 'x , for x, 'y for y and 'r for the result. Thus our function will look like:

fn longest<'x, 'y, 'r>(x: &'x str, y: &'y str) -> &'r str

But this is not enough. We still need to tell the compiler what the relationships are. There are two way to do it (the magic syntax will be explained later):

  • Inside the <> brackets like that: <'a, 'b: 'a>
  • In a where clause like that: where 'b: 'a

Both options are the same but the where clause will be more readable if you have a large number of generic parameters.

Back to the problem. We need to tell the compiler that 'r depends on both 'x and 'y and that it will be valid as long as they are valid. We can do that by saying 'long: 'short which translates to "lifetime 'long must be valid at least as long as lifetime 'short".

Thus we need to modify our function like that:

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

Ie we are saying that our returned value will not outlive the function parameters, thus preventing a "use after free" situation.

PS: In this example you can actually do it with only one lifetime parameter, as we are not interested in the relationship between them. In this case the lifetime will be the smaller one of x/y:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM