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

Borrow checker doesn't realize borrow ends in both branches #57956

Closed
jonhoo opened this issue Jan 28, 2019 · 5 comments
Closed

Borrow checker doesn't realize borrow ends in both branches #57956

jonhoo opened this issue Jan 28, 2019 · 5 comments
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jonhoo
Copy link
Contributor

jonhoo commented Jan 28, 2019

The following code fails to compile, even though there are no outstanding references to procs after the if let:

use std::sync::Mutex;
let g = Mutex::new(Mutex::new(String::new()));
let procs = g.lock().unwrap();
let q = procs.lock().ok();
let _sp = if let Some(_p) = q {
    Some(0)
} else {
    None
};
drop(procs);

with

error[E0505]: cannot move out of `procs` because it is borrowed
  --> src/main.rs:13:6
   |
7  | let q = procs.lock().ok();
   |         ----- borrow of `procs` occurs here
...
13 | drop(procs);
   |      ^^^^^ move out of `procs` occurs here
14 | }
   | - borrow might be used here, when `q` is dropped and runs the destructor for type `std::option::Option<std::sync::MutexGuard<'_, std::string::String>>`

If the if let is replace with an equivalent map on the other hand, the code compiles just fine:

use std::sync::Mutex;
let g = Mutex::new(Mutex::new(String::new()));
let procs = g.lock().unwrap();
let q = procs.lock().ok();
let _sp = q.map(|_p| {
    0
});
drop(procs);

This could be related to #47680 or #57165, I'm not sure. Happens on both 2015 and 2018 editions.

@jonhoo
Copy link
Contributor Author

jonhoo commented Jan 28, 2019

Interestingly enough, this works:

use std::sync::Mutex;
let g = Mutex::new(Mutex::new(String::new()));
let procs = g.lock().unwrap();
let mut q = procs.lock().ok();
let _sp = if let Some(ref _p) = q {
    Some(0)
} else {
    None
};
drop(q);
drop(procs);

Though the explicit drop(q) is needed.

@jonas-schievink jonas-schievink added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-NLL Area: Non-lexical lifetimes (NLL) labels Jan 28, 2019
@jonhoo
Copy link
Contributor Author

jonhoo commented Jan 28, 2019

Looking at it some more, I guess dropping is the key here. The map version and the version with Some(ref _p) both explicitly drop q before procs, whereas the failing version assumes that q is dropped before procs. But of course, that's not true, since q is implicitly dropped at the end of scope. So, I think this is a user issue 👼

@jonhoo jonhoo closed this as completed Jan 28, 2019
@jonhoo
Copy link
Contributor Author

jonhoo commented Jan 28, 2019

Actually, on second though, I think the first code sample should work, since q should presumably be dropped when we destruct it with the if let Some?

@jonhoo jonhoo reopened this Jan 28, 2019
@goffrie
Copy link
Contributor

goffrie commented Jan 30, 2019

Actually, on second though, I think the first code sample should work, since q should presumably be dropped when we destruct it with the if let Some?

if let Some(_p) = q doesn't actually destruct q as such - it moves out _p in the Some case, but overall leaves q partially initialized. If you force a full move from q by using if let Some(_p) = {q}, or by using q.map(|_p| 0), your snippet compiles.

@jonhoo
Copy link
Contributor Author

jonhoo commented Jan 30, 2019

Ah, yes, you're right! Thanks!

@jonhoo jonhoo closed this as completed Jan 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants