2

In the rust code below, I would expect both calls to pass_through to fail, since both a and b go out of scope at the end of the inner block. However, for some reason the following happens:

  • Creating a reference to an NC value and and storing that reference in a allows it to live long enough.
  • Creating an NC value and storing it in b, then taking a reference to b for the function parameter causes an error.
#[derive(Debug)]
struct NC(i32);

fn pass_through(x: &NC) -> &NC {
    x
}

fn main() {
    let a2: &NC;
    let b2: &NC;
    {
        let a = &NC(1);
        a2 = pass_through(a);  // Works fine

        let b = NC(2);
        b2 = pass_through(&b); // Error, borrowed value does not live long enough
    }
    println!("a2 - {:?}", a2);
    println!("b2 - {:?}", b2);
}

Why does the borrow checker treat these two cases differently?

user3840170
  • 26,597
  • 4
  • 30
  • 62
Wilduck
  • 13,822
  • 10
  • 58
  • 90
  • 6
    I am guessing `&NC(1)` has `'static` lifetime (since the expression is evaluated at compile time and written into the program text), but `&b` has the lifetime of `b`. – user3840170 May 21 '22 at 14:05
  • And that's the problem if you have a language without specification. Don't get me wrong, I love Rust, but I think a formal specification should exist. – Finomnis May 21 '22 at 15:10
  • You don't need the `pass_through` function, `= a` and `= &b` work the same – Finomnis May 21 '22 at 15:41

2 Answers2

3

This works for the let a case because &NC(1) refers to a read-only static that is automatically created by the compiler. The inferred type of a includes the lifetime.

// These two lines are equivalent:
let a = &NC(1);
let a: &'static NC = &NC(1);

The 'static lifetime is valid for the entire program, so there won't ever be a lifetime error assigning a 'static reference to something. The line a2 = pass_through(a); works because the lifetime of a2 is unrelated to the lifetime of a. All this does is copy the reference stored in a into a2, therefore a2 does not depend on a's lifetime at all. They are both &NC variables that refer to the same value. I think this is what you were confused about.

The b case fails because the b2 reference outlives the lifetime of b.

It's important to note that there is a hidden lifetime parameter in the signature of fall_through. It's as though you wrote this:

fn pass_through<'a>(x: &'a NC) -> &'a NC {

This just means that the lifetime of the returned reference is the same as the lifetime of the input reference. In the pass_through(a) call, the lifetime parameter 'a becomes 'static. In the pass_through(b) call, the lifetime parameter 'a matches the lifetime of b. In other words, this function is a simple identity function -- it returns its argument exactly, both in terms of its value and its type/lifetime.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • Thanks for this answer! Based on the other similar question linked above, It seems like `a` is given a static lifetime due to RFC-1414 (https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md), correct? – Wilduck May 22 '22 at 13:14
  • @Wilduck Correct, and so the type of `a2` is also inferred to be `&'static NC`. – cdhowie May 22 '22 at 13:16
0

Perhaps this is due to the way the compiler determines the lifetime of a reference.

In the case of the variable a, the compiler, analyzing the entire block, sees that the longest lifetime of the variable a2, which will be assigned a reference, extends its lifetime, because the variable does not own the data.

However, in the case of b, everything is different, the variable owns the data, which means that when you exit the block, the data will be deleted and the link will be valid.

Danila
  • 53
  • 6