About scoped borrower

In the following code

struct U;
fn test<'a>() {
  let x = U;
  let y : &'a _ = &x;
}

from the compiler error it seems that y is marked to have the same lifetime as test, which is longer than the lifetime of x , can't compile , so I put y in a block , expect that this will shorten the lifetime of y

struct U;
fn test<'a>() {
  let x = U;
  {
    let y : &'a _ = &x;
  }
}

but this does not work. Can I conclude that borrow checker will ignore the block scope when checking ?

I think you're misunderstanding the first snippet. The reason it doesn't compile is that 'a could be any arbitrarily long lifetime, so it may not be sound to borrow the local variable x with that lifetime. For example, some other code in the file could call test::<'static>(), and then in the body of test you'd be trying to borrow a local variable for 'static i.e. the whole remaining duration of the program, which is unsound since local variables are dropped when test returns.

tried this syntax , can't compile.

(For reference, the error—well, the one I assume you're running into—is "cannot specify lifetime arguments explicitly if late bound lifetime parameters are present".)

Yes, that's an annoying issue—the compiler decides that test actually has a higher-order lifetime, like for<'a> fn(), and such higher-order lifetimes cannot be "turbofished" for reasons I honestly don't understand. A (bizarre) workaround is to change the lifetime parameter from <'a> to <'a: 'a>, that is, add a trivially-satisfied outlives requirement, since this forces 'a to be early-bound = not higher-order. Check out this previous thread. This is all peripheral to what you were asking about, though.

One: If you have an unconstrained generic lifetime on your function like this, the caller chooses the lifetime, so it could be anything -- including 'static ("forever"). So when you're testing upper boundaries like this, you might as well test with 'static. Rewriting your example:

struct U;
fn test() {
  let x = U;
  let y: &'static _ = &x;
}

Two: If you have a variable with a certain scope of existence -- let's say, it doesn't get returned from the current function -- you can still put references with longer lifetimes into that variable, so long as they are valid:

// Compiles
fn test() {
  let y: &'static str = "";
}

That is, a variable doesn't have to exist as long as the references you put in it.

Three: Shortening the lifetime/existence of y is not going to help you. It's already shorter than the reference and making y shorter -- or longer! -- is not going to change the lifetime of the borrow in this scenario. Once you say the borrow must be 'static or some caller-chosen 'a, there is no changing the length of the borrow.

And that's the problem with the example: the thing you cannot do is take a reference for longer than the variable behind the reference exists. That's what you're running into -- x doesn't live long enough; x is not 'static; 'a can be longer than x exists for.

You need to shorten the length of the borrow (reference) stored inside y, or lengthen the lifetime of the variable being borrowed (x).

y doesn't really have anything to do with it.

// We already saw making `y` shorter doesn't help.
// No `y` and this function "lives forever", but this is still an error too
fn borrow_statically(_: &'static U) {}
fn test() {
  let x = U;
  borrow_statically(&x);
}

// This one is fine; `X` lives long enough
fn test() {
  static X: U = U;
  borrow_statically(&X);
}
// So is this
fn test<'a>() {
  static X: U = U;
  let y: &'a _ = &X;
}
// So is this (the borrow is short enough / not forced to be long)
fn test() {
  let x = U;
  let y = &x;
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.