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

Weird error for mutable references in a loop #70255

Open
RReverser opened this issue Mar 21, 2020 · 8 comments
Open

Weird error for mutable references in a loop #70255

RReverser opened this issue Mar 21, 2020 · 8 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@RReverser
Copy link
Contributor

I tried this code:

fn noop(v: &mut i32) -> Option<&mut i32> {
    Some(v)
}

pub fn f(v: &mut Vec<i32>) -> Option<&mut i32> {
    for item in v {
        if let Some(res) = noop(item) {
            return Some(res);
        }
        let _ = &*item;
    }
    None
}

I expected to see this happen: code successfully compiles because mutable reference is used only temporarily in an if block.

Instead, this happened: Rust fails to compile with an error.

error[E0502]: cannot borrow `*item` as immutable because it is also borrowed as mutable
  --> <source>:10:17
   |
5  | pub fn f(v: &mut Vec<i32>) -> Option<&mut i32> {
   |             - let's call the lifetime of this reference `'1`
6  |     for item in v {
7  |         if let Some(res) = noop(item) {
   |                                 ---- mutable borrow occurs here
8  |             return Some(res);
   |                    --------- returning this value requires that `*item` is borrowed for `'1`
9  |         }
10 |         let _ = &*item;
   |                 ^^^^^^ immutable borrow occurs here

Without noop it also fails, although with slightly different error:

pub fn f(v: &mut Vec<i32>) -> Option<&mut i32> {
    for item in v {
        if let Some(res) = Some(item) {
            return Some(res);
        }
        let _ = &*item;
    }
    None
}

Error:

error[E0382]: borrow of moved value: `item`
 --> <source>:6:17
  |
2 |     for item in v {
  |         ---- move occurs because `item` has type `&mut i32`, which does not implement the `Copy` trait
3 |         if let Some(res) = Some(item) {
  |                                 ---- value moved here
...
6 |         let _ = &*item;
  |                 ^^^^^^ value borrowed here after move

Meta

rustc --version --verbose:

rustc 1.42.0 (b8cedc004 2020-03-09)
binary: rustc
commit-hash: b8cedc00407a4c56a3bda1ed605c6fc166655447
commit-date: 2020-03-09
host: x86_64-unknown-linux-gnu
release: 1.42.0
LLVM version: 9.0
Compiler returned: 0

(although reproduces in all Rust versions I could find)

@RReverser RReverser added the C-bug Category: This is a bug. label Mar 21, 2020
@jonas-schievink jonas-schievink added A-diagnostics Area: Messages for errors, warnings, and lints 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. and removed C-bug Category: This is a bug. labels Mar 22, 2020
@Centril Centril added the NLL-polonius Issues related for using Polonius in the borrow checker label Mar 22, 2020
@memoryruins
Copy link
Contributor

memoryruins commented Mar 22, 2020

The code is currently accepted under rustc's nightly flag -Z polonius (as Centril has already tagged), and appears similar to other issues tagged as NLL-polonius, e.g. #51545 and #54663

@RReverser
Copy link
Contributor Author

Oh, you're right, the reduced example in #54663 and mine match almost exactly, so I'll close this issue to avoid the noise.

It's first time I hear about polonius TBH, is there anything to read about timeline and differences with current NLL implementation?

@memoryruins
Copy link
Contributor

The following posts go a bit in depth on polonius (https://github.com/rust-lang/polonius)

The first thing to note is that this proposal makes no difference from the point of view of an end-user of Rust. That is, the borrow checker ought to work the same as it would have under the NLL proposal, more or less.
However, there are some subtle shifts in this proposal in terms of how the compiler thinks about your program, and that could potentially affect future language features.

As for timeline, I’m unaware if one is set, but progress is being made by the working group! https://rust-lang.github.io/compiler-team/working-groups/polonius/

@RReverser
Copy link
Contributor Author

@memoryruins Thanks!

By the way, I just double-checked, and while first snippet does compile under -Zpolonius, the second one still fails.

Given that this part seems different from other linked issues (for them polonius does fix the problem), I'm hesitantly reopening this one for tracking.

@RReverser RReverser reopened this Mar 23, 2020
@memoryruins
Copy link
Contributor

Ah my mistake, thanks for double-checking and reopening!
These changes also allow the second sample to compile (with -Z polonius):

- if let Some(res) = Some(item) {
+ if let Some(res) = Some::<&mut _>(item) {
+ let item_: &mut _ = item;
- if let Some(res) = Some(item) {
+ if let Some(res) = Some(item_) {
- if let Some(res) = Some(item) {
+ if let Some(res) = Some(&mut *item) {

I'll leave it to someone else to explain if this is expected or not :)

@JakkuSakura
Copy link

JakkuSakura commented Aug 24, 2020

I ran into this error a few days ago. My solution was to use raw pointer to work around.

loop {
    if let Ok(x) = self.try_recv_ref() {
        unsafe {
            return &*(x as *const T);
        }
     }
}

Is there a way to solve this problem without nightly or unsafe?

@RReverser
Copy link
Contributor Author

@qiujiangkun Can you share the full working snippet on play.rust-lang.org? In my case, I found than intermediate boolean variable helped as an ugly but working workaround, maybe it's possible in yours too.

@JakkuSakura
Copy link

JakkuSakura commented Aug 25, 2020

@qiujiangkun Can you share the full working snippet on play.rust-lang.org? In my case, I found than intermediate boolean variable helped as an ugly but working workaround, maybe it's possible in yours too.

Sure.
playground
How can an intermediate boolean help out?

@fmease fmease added fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. and removed NLL-polonius Issues related for using Polonius in the borrow checker labels Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. 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

6 participants