Skip to content

RefCell::borrow in if let expression is not dropped until after else block #113792

@froody

Description

@froody

I tried this code:

use std::cell::RefCell;
use std::ops::Deref;

fn test(foo: RefCell<Option<u32>>) {
    if let Some(ref x) = foo.borrow().deref() {
        println!("got some {:?}",x);
    } else {
        println!("got none");
       *(foo.borrow_mut()) = Some(8);
    }
    println!("after {:?}", foo);
    *(foo.borrow_mut()) = Some(9);
    println!("finally {:?}", foo);
}

fn main() {
    test(RefCell::new(Some(7)));
    test(RefCell::new(None));
}

I expected to see this happen:

got some 7
after RefCell { value: Some(7) }
finally RefCell { value: Some(9) }
got none
after RefCell { value: Some(8) }
finally RefCell { value: Some(9) }

Instead, this happened:

got some 7
after RefCell { value: Some(7) }
finally RefCell { value: Some(9) }
got none
thread 'main' panicked at 'already borrowed: BorrowMutError', wat.rs:9:14

IMHO, the borrowed value should go out of scope before the body of the else branch. Trying to bind the temporary to a variable fails when using it in the else branch:

fn test(foo: RefCell<Option<u32>>) {
    if let y @ Some(ref x) = foo.borrow().deref() {
        println!("got some {:?} {:?}",x, y);
    } else {
        println!("got {}", y);
       *(foo.borrow_mut()) = Some(8);
    }
    println!("after {:?}", foo);
    *(foo.borrow_mut()) = Some(9);
    println!("finally {:?}", foo);
}

Note that the borrow checker is perfectly ok with the equivalent code not using a RefCell:

fn test(foo: &mut Option<u32>) {
    if let Some(ref x) = foo {
        println!("got some {:?}",x);
    } else {
        println!("got none");
       *foo = Some(8);
    }
    println!("after {:?}", foo);
    *foo = Some(9);
    println!("finally {:?}", foo);
}

fn main() {
    let mut tmp = Some(7);
    test(&mut tmp);
    tmp = None;
    test(&mut tmp);
}

Meta

Also repro's on 1.73.0-nightly (2023-07-16 0e8e857b11f60a785aea)

rustc --version --verbose:

rustc 1.70.0 (90c541806 2023-05-31)
binary: rustc
commit-hash: 90c541806f23a127002de5b4038be731ba1458ca
commit-date: 2023-05-31
host: aarch64-apple-darwin
release: 1.70.0
LLVM version: 16.0.2
Backtrace

got some 7
after RefCell { value: Some(7) }
finally RefCell { value: Some(9) }
got none
thread 'main' panicked at 'already borrowed: BorrowMutError', wat.rs:9:14
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/panicking.rs:578:5
   1: core::panicking::panic_fmt
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/panicking.rs:67:14
   2: core::result::unwrap_failed
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/result.rs:1687:5
   3: core::result::Result<T,E>::expect
   4: core::cell::RefCell<T>::borrow_mut
   5: wat::test
   6: wat::main
   7: core::ops::function::FnOnce::call_once

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions