Search code examples
rustlifetime-scoping

Lifetime of temporary objects in if let statements


How come the lifetime of Value(1) and Value(2) differs in the code below?

#![allow(irrefutable_let_patterns)]

struct Value(i32);

impl Drop for Value {
    fn drop(&mut self) {
        println!("Dropping Value({})", self.0);
    }
}

pub fn main() {
    if Value(1).0 == 1 {
        println!("one!");
    };

    if let _ = Value(2).0 {
        println!("two!");
    };
}

Value(1) is dropped before "one!", while Value(2) is dropped after "two!"

playgroud

I would have expected both Values to be dropped before executing the println!("one!/two!") statements.


Solution

  • (Edited to actually explain the difference between the first and the second if)

    The order of the first if's drop call is due to the layout of scopes in your program. You can see an example similar to yours in the Rust reference (temporary scopes). The main function should have those related scopes:

    1. the function body;
    2. the first if's condition expression scope;
    3. the first if's body;

    What matters for your question is point 2. A temporary scope is created while evaluating the condition of an if statement. Your Value struct exists only in this temporary scopes. When the if's body is entered, the condition scope is popped and its initialized variables are dropped. That's why Value's drop method is executed before your println! instruction in your if statements.

    Regarding the if let expression, as the reference says the drop scopes are determined after replacing it with the equivalent match expression:

    match Value(2).0 {
        _ => { println!("two!"); }
    }
    

    The match scrutinee expression is kept alive until the end of the match, to allow match arms to borrow from the scrutinee expressions (see related GitHub issue).