Previous topics:
- From 2019: [Pre-RFC] Destructuring values that impl Drop.
- From 2014: Destructuring structs which implement Drop - #12 by tbu.
Proposal
Turn error E0509 into a lint, warning by default:
- This still allows users to catch unintended destructuring which would bypass
Drop
. - While allowing users to override the compiler, when desired.
This differs from the 2019 proposal in that instead of a custom attribute and a lint, this proposal contends that just a lint is enough, since lints can be tuned. The 2019 proposal did list this possibility in its conclusion.
Motivation
The following code, while seemingly reasonable, is rejected:
struct Dummy {
foo: i32,
bar: String,
}
impl Drop for Dummy {
fn drop(&mut self) {
println!("Dropping Dummy {{ foo: {}, bar: {} }}", self.foo, self.bar);
}
}
fn main() {
let dummy = Dummy { foo: 2, bar: String::from("3") };
let Dummy { foo, bar } = dummy;
println!("{foo} + {bar} = 5");
}
Currently leads to:
error[E0509]: cannot move out of type `Dummy`, which implements the `Drop` trait
--> src/main.rs:14:30
|
14 | let Dummy { foo, bar } = dummy;
| --- ^^^^^ cannot move out of here
| |
| data moved here
| move occurs because `bar` has type `String`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
14 | let Dummy { foo, ref bar } = dummy;
| +++
For more information about this error, try `rustc --explain E0509`.
error: could not compile `playground` (bin "playground") due to 1 previous error
I do think warning the user that they may accidentally be skipping the Drop
implementation of the type is a good idea, they may not have intended to. I do think that making this lint into a hard error is taking things one step too far.
The Rationale section of the 2019 proposal already offers a comprehensive list of reasons for doing this.
Q & A
But invoking
drop
on the instance would be UB!
Why yes, obviously. Hence the compiler wouldn't.
If an instance is moved then drop
isn't invoked on it. If a destructuring is partial, then drop
is only invoked on the fields not matched.
The new "move-destructure-for-droppable-type" capability would just follow those two existing rules.
But this allows users to bypass
drop
!
Users can already bypass drop, and do so routinely. The very forget
function allows a user to bypass drop
, and it's otherwise possible to leak types, etc..
Should it be restricted to the crate or module where
drop
is defined?
There'd be no point, really.
Destructuring is already restricted via visibility rules: only fields that are accessible via .
are accessible via destructuring.
Users can already, painstakingly, swap out visible fields then forget
the instance. Destructuring would not give them any more power, it'd just make the code easier to read.
The warning would remind users that bypassing Drop
may lead to leaks, and from then on it's up to them.