Moving mutable borrows in/out of inferred types results in the compiler thinking they are moved as if they were owned values
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the minimal Vec<&mut Person> reproduction and compare it with the explicitly typed version. Then inspect compiler/src/type_check/expressions.rs around the reported loop and run method to trace how the inferred element type is handled. Done means the unannotated examples compile with the borrow moved back into the collection, with regression coverage for the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100