0

Currently, I'm learning the Lifetimes topic in Rust.
And I kind of get it, but recently I've came across this simple code:

#[derive(Debug)]
struct Test {
    val: i32
}

fn main() {
    let a:&Test;
    let b = 32;

    a = &Test {
        val: b
    };

    println!("{:?}", a.val);
}

But it won't compile, with the respective error provided:

error[E0716]: temporary value dropped while borrowed

I know how to fix it & I do understand, that a temporary was created at line 10 and will be destroyed at the end of the statement at line 12.
(also the compiler itself states the same)

What I don't understand, it's why the following code actually will compile?

#[derive(Debug)]
struct Test {
    val: i32
}

fn main() {
    let a:&Test;
    let b = 32;

    a = &Test {
        val: 5
    };

    println!("{:?}", a.val);
}

Only struct initialization values has been changed.
The val was b and now it's 5.

My guess is that the value of b, which is now owned by val field of our struct instance will be dropped at the end of the statement.
And therefore we will end up with the dangling reference.

Oh ... and one more interesting example.

#[derive(Debug)]
struct Test {
    val: i32
}

fn main() {
    let a:&Test;
    let b = 32;

    let a = &Test {
        val: b
    };

    println!("{:?}", a.val);
}

This will also successfully compile.
It's the same as the first example, but we shadowing the a variable with let.

So, my fellow rustaceans ... why it's working this way?

Abraham Tugalov
  • 1,902
  • 18
  • 25
  • You have a misconheption about where the temporary gets dropped. In simple cases like this Rust constructs an inaccessible temporary variable which is dropped at the end of the scope, not at the end of the statement. – cafce25 Jan 13 '23 at 00:39
  • For now I can only close vote: Does this answer your question? [Why is it legal to borrow a temporary?](https://stackoverflow.com/questions/47662253/why-is-it-legal-to-borrow-a-temporary) – cafce25 Jan 13 '23 at 00:47
  • @cafce25 No, it doesn't. – Abraham Tugalov Jan 13 '23 at 00:48
  • 1
    @AbrahamTugalov Why not? – Chayim Friedman Jan 13 '23 at 00:48
  • 1
    best way to learn rust is to ignore lifetime until you really really need them, and this would come only after code in rust for 2 years and you want code a library – Stargateur Jan 13 '23 at 02:13

1 Answers1

-3

Case 1

    a = &Test { val: b };
    println!("{a:?}");

Let's call this one the base case. No "magic" happens, the temporary value is dropped at the end of the statement, and the compiler complains "temporary value dropped while borrowed".

Case 2

    a = &Test { val: 5 };
    println!("{a:?}");

The key difference here is we now have a constant expression due to the literal 5, so it is eligible for constant promotion:

Promotion of a value expression to a 'static slot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior.

Case 3

    let a = &Test { val: b };
    println!("{a:?}");

Finally, here we have a let statement, which is eligible for temporary lifetime extension. According to the reference,

The temporary scopes for expressions in let statements are sometimes extended to the scope of the block containing the let statement

but also,

The exact rules for temporary lifetime extension are subject to change. This is describing the current behavior only

Evidently, temporary lifetime extension for let statements doesn't occur when the let statement is separated from its initializer.

jw013
  • 1,718
  • 17
  • 22