Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temporary lifetime extension applies to the last expression of a for loop body, causing unnecessary errors #118544

Open
kpreid opened this issue Dec 2, 2023 · 1 comment
Labels
A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug.

Comments

@kpreid
Copy link
Contributor

kpreid commented Dec 2, 2023

I tried this code (reduced from https://users.rust-lang.org/t/lifetime-of-refcell-borrows/103346):

use std::cell::RefCell;

fn foo() {
    let mut output = Vec::new();

    for cell in vec![RefCell::new(Some(1))] {
        let cell = cell;

        if let Some(value) = cell.borrow().clone() {
            output.push(value);
        }
    }
}

The following error results:

error[E0597]: `cell` does not live long enough
  --> src/lib.rs:9:30
   |
7  |         let cell = cell;
   |             ---- binding `cell` declared here
8  |
9  |         if let Some(value) = cell.borrow().clone() {
   |                              ^^^^---------
   |                              |
   |                              borrowed value does not live long enough
   |                              a temporary with access to the borrow is created here ...
...
12 |     }
   |     -
   |     |
   |     `cell` dropped here while still borrowed
   |     ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, std::option::Option<i32>>`
   |
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
   |
11 |         };
   |          +

The suggested added semicolon does indeed let the code compile, but it really shouldn't be necessary because:

  • A for loop cannot ever return a value other than ()
  • An if let without else cannot ever return a value other than () (even if the pattern is irrefutable)

It's also weird that let cell = cell; (or some other local let binding) is needed to trigger the problem; presumably this happens because there's a drop scope in between the binding of the loop's pattern and the body block, but it's not at all obvious that the pattern should be longer-lived in that way.

Meta

rustc --version --verbose:

rustc 1.76.0-nightly (6b771f6b5 2023-11-15)
binary: rustc
commit-hash: 6b771f6b5a6c8b03b6322a9c77ac77cb346148f0
commit-date: 2023-11-15
host: x86_64-apple-darwin
release: 1.76.0-nightly
LLVM version: 17.0.5

Also tested on stable 1.74.0 on the Playground.

@kpreid kpreid added the C-bug Category: This is a bug. label Dec 2, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 2, 2023
@fmease fmease added the A-lifetimes Area: Lifetimes / regions label Dec 2, 2023
@saethlin saethlin removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 3, 2023
@ShoyuVanilla
Copy link
Member

ShoyuVanilla commented Feb 6, 2024

This issue seems to be the subset of #21114 and it was explained as non-bug in similar issue's comment.
And the diagnostic to add a trailing semicolon at the end of the scope is quite intentional by #54556.
So I think that this issue could be marked as closed.
I've spent few hours for trying to resolve this issue before searching it, TBH 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

5 participants