Description
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