rust

semicolon at the end of match expression


In the below code I'm getting borrow checker related issue if I comment out the println! statement after the match expression. And to fix this issue the compiler is asking me to put a semicolon at the end of match expression which solves the issue but couldn't understand the need of it.

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

#[derive(Default)]
struct A {
    b: Arc<Mutex<B>>,
}

#[derive(Default)]
struct B {
    c: Arc<Mutex<C>>,
}

#[derive(Default)]
struct C {
    d: i32,
}

fn main() {
    let a = A::default();
    
    match a.b.lock().unwrap().c.lock().unwrap().d {
        0 => println!("zero"),
        _ => println!("non-zero"),
    }
    // uncommenting the below line makes borrow checker happy
    // println!("{}", a.b.lock().unwrap().c.lock().unwrap().d);
}

Error

error[E0597]: `a.b` does not live long enough
  --> src/main.rs:21:11
   |
19 |     let a = A::default();
   |         - binding `a` declared here
20 |     
21 |     match a.b.lock().unwrap().c.lock().unwrap().d {
   |           ^^^----------------
   |           |
   |           borrowed value does not live long enough
   |           a temporary with access to the borrow is created here ...
...
27 | }
   | -
   | |
   | `a.b` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
   |
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
   |
24 |     };
   |      +

For more information about this error, try `rustc --explain E0597`.

Solution

  • They changed it in the 2024 edition.

    you can try in playground, if you use nightly, it'll compile but not on stable.

    https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html

    The 2024 Edition changes the drop order of temporary values in tail expressions. It often comes as a surprise that, before the 2024 Edition, temporary values in tail expressions can live longer than the block itself, and are dropped later than the local variable bindings, as in the following example:

    // Before 2024
    fn f() -> usize {
       let c = RefCell::new("..");
       c.borrow().len() // error[E0597]: `c` does not live long enough
    }
    

    In 2024, the temporaries of the tail expression may now be dropped immediately at the end of the block (before any local variables in the block).