Description
The ownership check for move closures is broken in Rust 2021 and 2024 editions, but works correctly in 2015 and 2018 editions.
Reproduction
#[derive(Debug)]
struct Point { x: i32, y: i32 }
fn main() {
let mut a = Point { x: 1, y: 2 };
let mut add = move |x, y| {
a.x += x;
a.y += y;
};
add(10, 10);
println!("{:?}", a); // Should error but doesn't in 2021/2024
}
rustc 1.90.0 (1159e78c4 2025-09-14)
binary: rustc
commit-hash: 1159e78c43dd055665addee1ca6e30c4251b9b0a
commit-date: 2025-09-14
host: x86_64-pc-windows-msvc
release: 1.90.0
Expected behavior (as in 2015/2018 editions)
Compilation error E0382: borrow of moved value a
Actual behavior in 2021/2024 editions
Code compiles without error
Output: Point { x: 1, y: 2 } (closure has no effect)
Test results
✅ rustc --edition=2015 main.rs: Correct error
✅ rustc --edition=2018 main.rs: Correct error
❌ rustc --edition=2021 main.rs: Incorrectly compiles
❌ rustc --edition=2024 main.rs: Incorrectly compiles
I believe this is an intended change in Rust 2021. See "Disjoint capture in closures" in the Edition Guide ; note that the summary starts with "|| a.x + 1 now captures only a.x instead of a."
1 Like
Intentional but worthy of a lint (which we don't have yet) in the face of Copy types:
opened 12:27AM - 09 May 23 UTC
A-lints
A-closures
T-compiler
C-bug
I noticed some counterintuitive behavior related to partial moves of Copy struct… fields (ex: u64 fields) into closures.
## Minimal Reproducible Example
```rust
#[derive(Debug)]
struct Statistics {
calls: u64,
successes: u64,
}
fn main() {
let mut statistics = Statistics {
calls: 0,
successes: 0,
};
let mut generator = move || {
statistics.calls += 1;
};
generator();
statistics.successes += 1;
println!("{statistics:?}");
}
```
This code compiles and prints
```
Statistics { calls: 0, successes: 1 }
```
[Playground Link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c4180fa1795ad2b77b0c0aa28aeaf37a)
## What I Expected
I would have expected a `borrow of partially moved value: 'statistics'` error at the `println!`, since the closure takes ownership of the `statistics.calls` field and then the field is later read by the print call.
The actual behavior, where the updates to `statistics.calls` have no observable effect, looks just like a silent copy of the whole struct. This surprised me. When I wrote the code, I expected either a compiler error or an output of `Statistics { calls: 1, successes: 1 }`.
## Investigation
I experimented a bit with the types of the fields within the struct, and I do get the compiler error I expect if `Statistics.calls` is a non-Copy type like `Vec`.
I didn't know about partial moves when I wrote the initial code. I expected the whole struct to move into the closure. It's still surprising to me that partial moves can create this type of "silent copy" behavior depending on the type of the struct's fields.
I did find a similar issue titled [Move closure copies :Copy variables silently](https://github.com/rust-lang/rust/issues/63220), but the code that I wrote is not covered by the "unused variable" warning added in response to that issue.
## Solution
I'm new to Rust so I'm not sure this is a bug, but it's still confusing behavior. Perhaps, like the other issue I linked, there is room for a lint or warning here. Something that warned me that the closure's modification of `statistics.calls` had no effect would be helpful, for example.
opened 03:59AM - 18 Jun 20 UTC
A-lints
A-closures
T-compiler
C-bug
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6dee1348… 129e0c1938c67eb66a8f62b0
```rust
fn main() {
let mut x = 0;
let closure = move || {
let mut closure2 = move || {
x += 1;
};
closure2();
println!("{}", x);
};
closure();
}
```
The inner closure essentially forks the mutable variable `x` into *two* variables. I would expect rustc to give a warning or error, but there is no warning or error. I understand the logic behind allowing this but I think it can lead to surprising results. rust-analyzer warns that assignment to `x` in `closure2` is never read. This led me to a real bug in our code.
Seems to me that duplicating a `mut` `Copy` variable like this deserves some kind of warning at least.
1 Like
system
Closed
December 31, 2025, 11:08pm
5
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.