rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.7k stars 12.49k forks source link

Compile fail with implicit return #94980

Open marcbowes opened 2 years ago

marcbowes commented 2 years ago

I tried this code:

use std::sync::{Arc, Mutex, Weak};

fn main() {
    let value = Arc::new(Mutex::new(String::new()));
    let weak = Arc::downgrade(&value);
    let _ = foo(weak);
}

fn foo(weak: Weak<Mutex<String>>) -> usize {
    let upgraded = weak.upgrade().unwrap();
    match upgraded.try_lock() {
        Ok(guard) => guard,
        _ => unreachable!(),
    }
    .len()
}

I expected to see this happen: it compiles!

Instead, this happened:

error[E0597]: `upgraded` does not live long enough
  --> src/main.rs:11:11
   |
11 |     match upgraded.try_lock() {
   |           ^^^^^^^^^^^^^^^^^^^
   |           |
   |           borrowed value does not live long enough
   |           a temporary with access to the borrow is created here ...
...
16 | }
   | -
   | |
   | `upgraded` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<MutexGuard<'_, String>, TryLockError<MutexGuard<'_, String>>>`
   |
   = note: the temporary is part of an expression at the end of a block;
           consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
   |
11 ~     let x = match upgraded.try_lock() {
12 |         Ok(guard) => guard,
13 |         _ => unreachable!(),
14 |     }
15 ~     .len(); x
   |

For more information about this error, try `rustc --explain E0597`.
error: could not compile `explret` due to previous err

What's weird is changing to an explicit return fixes the problem:

fn foo(weak: Weak<Mutex<String>>) -> usize {
    let upgraded = weak.upgrade().unwrap();
    return match upgraded.try_lock() {
        Ok(guard) => guard,
        _ => unreachable!(),
    }
    .len();
}

Meta

rustc --version --verbose:

rustc 1.59.0 (9d1b2106e 2022-02-23)
binary: rustc
commit-hash: 9d1b2106e23b1abd32fce1f17267604a5102f57a
commit-date: 2022-02-23
host: x86_64-apple-darwin
release: 1.59.0
LLVM version: 13.0.0

theemathas commented 3 weeks ago

The error is correct. An explicit return causes the drop order of temporaries to change. For example, see the following code:

struct Loud(i32);

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

fn dummy(_: &Loud) -> i32 { 1 }

fn foo() -> i32 {
    let _x = Loud(1);
    dummy(&Loud(2))
}

fn bar() -> i32 {
    let _x = Loud(3);
    return dummy(&Loud(4));
}

fn main() {
    foo();
    bar();
}

Output:

dropped 1
dropped 2
dropped 4
dropped 3

See also #69367