Skip to content

Moving mutable borrows in/out of inferred types results in the compiler thinking they are moved as if they were owned values #129694

Open
@yorickpeterse

Description

@yorickpeterse

Apologies for the slightly long title, but I'm not sure how to best summarize this issue. The problem is as follows: I have some code that uses a stack of sorts (VecDeque in my case, but the exact type isn't relevant) and pops data from it. It then does something with the data, and optionally pushes it back onto the stack. Crucially, the data is a mutable borrow of something, i.e. &mut Whatever. The code roughly looks like this:

let mut work = VecDeque::new();

...

while let Some((module, node)) = work.pop_front() {
    if pass.run(module, node) {
        resolved = true;
    } else {
        work.push_back((module, node));
    }
}

...

Here node is a &mut Something. The problem I'm running into is that type-checking this code results in the following error:

   --> compiler/src/type_check/expressions.rs:632:45
    |
628 |             while let Some((module, node)) = work.pop_front() {
    |                                     ---- move occurs because `node` has type `&mut hir::DefineConstant`, which does not implement the `Copy` trait
629 |                 if pass.run(module, node) {
    |                                     ---- value moved here
...
632 |                     work.push_back((module, node));
    |                                             ^^^^ value borrowed here after move
    |
note: consider changing this parameter type in method `run` to borrow instead if owning the value isn't necessary
   --> compiler/src/type_check/expressions.rs:659:15
    |
656 |     fn run(
    |        --- in this method
...
659 |         node: &mut hir::DefineConstant,
    |               ^^^^^^^^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value

For more information about this error, try `rustc --explain E0382`.
error: could not compile `compiler` (lib) due to 1 previous error

In other words, it seems that the compiler treats node as if it were an owned value and thus applies move semantics to it, but it's a borrow instead.

This issue can in fact be resolved by giving work an explicit type annotation like so:

let mut work: VecDeque<(ModuleId, &mut hir::DefineConstant)> = VecDeque::new();

This suggests the issue is potentially due to type inference not inferring the correct type or ownership.

A very easy way to reproduce this is the following snippet:

struct Person {}

fn main() {
    let mut work = Vec::new();

    while let Some(person) = work.pop() {
        run(person);
        work.push(person);
    }
}

fn run(_person: &mut Person) {}

I expected to see this happen: it should just work, because person is a borrow

Instead, this happened: the compiler produces the following error:

    Checking playground v0.1.0 (/var/home/yorickpeterse/Projects/rust/playground)
error[E0382]: borrow of moved value: `person`
  --> src/main.rs:8:19
   |
6  |     while let Some(person) = work.pop() {
   |                    ------ move occurs because `person` has type `&mut Person`, which does not implement the `Copy` trait
7  |         run(person);
   |             ------ value moved here
8  |         work.push(person);
   |                   ^^^^^^ value borrowed here after move
   |
note: consider changing this parameter type in function `run` to borrow instead if owning the value isn't necessary
  --> src/main.rs:12:17
   |
12 | fn run(_person: &mut Person) {}
   |    ---          ^^^^^^^^^^^ this parameter takes ownership of the value
   |    |
   |    in this function
help: consider cloning the value if the performance cost is acceptable
   |
7  |         run(person).clone();
   |                    ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Like with my actual code, one can prevent this error by defining work as let mut work: Vec<&mut Person> = Vec::new(); instead of relying on type inference.

Meta

rustc --version --verbose:

rustc 1.80.1 (3f5fd8dd4 2024-08-06)
binary: rustc
commit-hash: 3f5fd8dd41153bc5fdca9427e9e05be2c767ba23
commit-date: 2024-08-06
host: x86_64-unknown-linux-gnu
release: 1.80.1
LLVM version: 18.1.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerC-discussionCategory: Discussion or questions that doesn't represent real issues.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions