3

When do unnamed values go out of scope, when is the value dropped?

I'm looking for an answer based on official docs, not based on experiments.

Example 1:

f(foo().bar());

Example 2:

match foo().bar() {
    // ...
}

If bar is fn bar(self) -> ... it takes ownership of the passed value, and it is dropped as usual, but what happens if bar borrows, i.e. fn bar(&self) -> ...? Does it matter whether the result of bar depends on the lifetime of &self?

That is, foo could be returning a MutexGuard; it is essential to know when the guard is dropped (and the mutex is unlocked).

The experimental method shows that the unnamed value is dropped after the statement it is created in is "finished"; to force the "early" drop a let statement is required.

Playground

#[derive(Debug)]
pub struct Foo;
pub fn foo() -> Foo {
    println!("foo()");
    Foo
}
impl Foo {
    pub fn bar(&self) {
    }
}
impl Drop for Foo {
    fn drop(&mut self) {
        println!("Foo::drop()");
    }
}

fn main() {
    println!("--- scope test start");
    println!("value: {:?}", foo().bar());
    println!("--- end");

    println!("--- scope test start");
    match foo().bar() {
        v => println!("value: {:?}", v),
    }
    println!("--- end");

    println!("--- scope test start");
    let v = foo().bar();
    println!("value: {:?}", v);
    println!("--- end");
}

prints:

--- scope test start
foo()
value: ()
Foo::drop()
--- end
--- scope test start
foo()
value: ()
Foo::drop()
--- end
--- scope test start
foo()
Foo::drop()
value: ()
--- end
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Stefan
  • 5,304
  • 2
  • 25
  • 44

1 Answers1

3

From the reference:

When using an rvalue in most lvalue contexts, a temporary unnamed lvalue is created and used instead, if not promoted to 'static. Promotion of an rvalue expression to a 'static slot occurs when the expression could be written in a constant, borrowed, and dereferencing that borrow where the expression was the originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g. &None always has the type &'static Option<_>, as it contains nothing disallowed). Otherwise, the lifetime of temporary values is typically

  • the innermost enclosing statement; the tail expression of a block is considered part of the statement that encloses the block, or

  • the condition expression or the loop conditional expression if the temporary is created in the condition expression of an if or an if/else or in the loop conditional expression of a while expression.

When a temporary rvalue is being created that is assigned into a let declaration, however, the temporary is created with the lifetime of the enclosing block instead, as using the enclosing statement (the let declaration) would be a guaranteed error (since a pointer to the temporary would be stored into a variable, but the temporary would be freed before the variable could be used). The compiler uses simple syntactic rules to decide which values are being assigned into a let binding, and therefore deserve a longer temporary lifetime.

The reference then has examples of these rules.

Jan Špaček
  • 1,111
  • 1
  • 9
  • 24
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366