# Drop bounds are useless, so let's change that

**URL:** <https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044>\
**Category:** language design\
**Created:** [June 17, 2024, 11:08pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044 "2024-06-17T23:08:26Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [June 17, 2024, 11:08pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/1 "2024-06-17T23:08:26Z")

</div>

Bounding a generic type on `Drop` is essentially useless, to the point there's even a default warn lint against it:

```plaintext
warning: bounds on `T: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped
 --> src/lib.rs:1:13
  |
1 | pub fn f<T: Drop>(_: T) {}
  | ^^^^
  |
  = note: `#[warn(drop_bounds)]` on by default

```

A trait bound on `Drop` requires that an explicit `impl` of `Drop` for the type is present. That this is writable is actually quite awkward for API stability as well — generally people want to consider replacing a `Drop` implementation with drop glue that accomplishes the same cleanup to be a compatible change, but explicit bounds on `Drop` mean that removing an impl is API breaking, and it doesn't fall under any category of allowed minor name resolution breakage.

On the other hand, an empty trait that is blanket implemented for all types (e.g. [`Destruct`](https://doc.rust-lang.org/stable/std/marker/trait.Destruct.html)) can actually be useful in some cases. Potential applications include:

- `dyn Destruct` as a maximally erased trait object where you have the vtable entries for `drop_in_place`, `size_of_val`, and `align_of_val` but nothing else. Like using `dyn Any` for when you want to hold ownership of something but don't need the ability to downcast and/or want to avoid `Any`'s `'static` requirement.
- `~const Destruct` for expressing `~const` bounds on drop glue.
- As part of an [unbound hierarchy](https://smallcultfollowing.com/babysteps/blog/2024/04/23/dynsized-unsized/)\[1\] where a bound on `Destruct` removes the default `Sized` bound and only provides the ability to `drop_in_place`.

I'd like to propose that we just forbid writing trait bounds on `Drop` over an edition boundary with the hope of eventually making `Drop` as a trait bound actually mean "can be dropped." Doing this would have the added benefit of making `!Drop` actually mean something closer to linear types like that syntax gets informally used instead of "doesn't have a `Drop` impl but could still have nontrivial drop glue."

The only thing that really suffers from such a change IMHO is that `Drop` and `Copy` (the trait bounds) aren't mutually exclusive anymore. What people actually mean when they say they're mutually exclusive still holds — `Copy` and nontrivial drop glue are still mutually exclusive — but it does become possible to have types that are `Copy + Destruct`.

* * *

1. The "less than `Sized`" capabilities/traits:

---

<div class="post-metadata">

**Author:** ![Neutron3529](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/neutron3529/32/6976_2.png) [@Neutron3529](https://internals.rust-lang.org/u/Neutron3529)\
**Post date:** [June 18, 2024, 1:52am UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/2 "2024-06-18T01:52:34Z")

</div>

What is the use case?

You could always call `drop(foo)` no matter whether `Foo: Drop` holds.

Currently, the bound just tell you "What you can do", but `Drop` is a negative bound, with `Drop`, you could only do less thing (for example, partial move).

> [@CAD97](#):
>
> `Drop` and `Copy` (the trait bounds) aren't mutually exclusive anymore.

`Copy` means you could directly use memcpy to create a new clone, `Drop` means you must call the drop guard when variable out of scope. It could be a terrible idea allowing `Drop+Copy`

---

<div class="post-metadata">

**Author:** ![Noratrieb](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/noratrieb/32/12440_2.png) [@Noratrieb](https://internals.rust-lang.org/u/Noratrieb)\
**Post date:** [June 18, 2024, 3:24pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/3 "2024-06-18T15:24:36Z")

</div>

There's a lint already, right? But even then, forbidding it altogether probably makes sense too. I don't think it's a good idea to ever allow them with changed semantics, that seems potentially confusing.

---

<div class="post-metadata">

**Author:** ![notriddle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/notriddle/32/14082_2.png) [@notriddle](https://internals.rust-lang.org/u/notriddle)\
**Post date:** [June 18, 2024, 7:47pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/4 "2024-06-18T19:47:48Z")

</div>

When I implemented this lint, I found out about this:

> **[lib.rs - source](https://docs.rs/pin-project-lite/0.2.14/src/pin_project_lite/lib.rs.html#1353-1369)**
>
> Source of the Rust file \`src/lib.rs\`.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [June 18, 2024, 9:36pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/5 "2024-06-18T21:36:40Z")

</div>

> [@notriddle](#):
>
> [link to pin projection macro]

Ah, yes, pin projection actually does need to reject a manual `Drop` implementation but is fine with the fields having drop glue of their own. Without an alternative solution, making drop bounds a hard error (or changing their meaning) isn't doable.

Personally, I conceptualize pin projection as always adding a `Drop` which shims to a pinned drop signature. This is simpler than provoking `impl Drop` to cause an error (as long as writing an empty pinned drop impl isn't necessary), but it does impact lifetime capture behavior when there are generics without drop glue\[1\].

There is an obvious alternative, though: allow the pin projection macro to just directly write what it wants: `impl !Drop for Self`. The full extent of negative impls' implications (e.g. on coherence) aren't fully clear yet (let alone fully implemented), but they are fundamentally required to exist since `impl !DerefMut for &_` and `impl !Clone for &mut _` are required to exist for soundness.

...but this has the same issue that `Drop` bounds currently have: `!Drop` looks like "cannot be dropped" instead of "only has automatic drop glue" which is what it actually means. Preventing the `Drop` impl using a negative impl is still preferable but surfaces the same confusion of `Drop` only being a portion of the type's drop glue and that types can have nontrivial drop glue without implementing `Drop`.

I want to be able to conceptualize the `Drop` trait as providing `drop_in_place` and `Drop::drop` just being a convenient way to compose its definition. This works by conceptualizing `Drop` has having a default implementation overridden by an explicit one. Unfortunately the need of library pin projection to interact with this "shallow" portion of the drop glue for soundness seems to make this impossible.

So `Drop` (has "whole item" drop glue in addition to its fields') and `Destruct` (can be dropped) are required to stay as separate concepts. I'm obviously not particularly fond of this reality -- the lint's existence indicates that this is known to be confusing -- but can at least accept that it's neccessary.

> [@Noratrieb](#):
>
> changed semantics, that seems potentially confusing

While I don't disagree, I thought of it as potentially acceptable since `Drop` bounds in the current editions are basically always in error and shouldn't exist (with the one exception of emulating a negative impl). So you could just ignore the old semantics and use the new ones, relying on `warn(drop_bounds)` to warn you when the bound isn't writable.

> [@Neutron3529](#):
>
> `Drop` means you must call the drop guard when variable out of scope.

No it doesn't; that's the core thesis of this thread. If I write `struct S(String);`, `S` does _not_ implement `Drop` (the bound `S: Drop` is not satisfied) but it still has nontrivial drop glue (`needs_drop::<S>()` is `true`). I don't even need to define a custom `S` for my example, as [this is the case for `String` already](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=73ffdf0d5a6e82b7422b8e8eced6c7a6):

```plaintext
error[E0277]: the trait bound `String: Drop` is not satisfied
 --> src/main.rs:5:18
  |
5 | bounded_drop(s);
  | ------------ ^ the trait `Drop` is not implemented for `String`
  | |
  | required by a bound introduced by this call
  |
note: required by a bound in `bounded_drop`
 --> src/main.rs:1:20
  |
1 | fn bounded_drop<T: Drop>(_: T) {}
  | ^^^^ required by this bound in `bounded_drop`

```

In the world described in the OP, `Drop` would be an always-satisfied trait bound (up until support for exotically unsized types exist, anyway). `T: Copy` would still imply that the type has trivial drop glue (that `drop::<T>` and `forget::<T>` are identical functions) and prevent a manual implementation of `Drop`; the only change would be to make a `T: Drop` bound mean "`T` can be dropped" instead of "dropping `T` requires more than just dropping its fields individually." Using `Drop` as a trait bound doesn't add capabilities, it takes them away (although those removed couldn't be used in a generic context anyway).

* * *

1. With a `Drop` implementation, `self` is "used" at drop timing. Without a `Drop` implementation, `self` isn't, but the fields of `self` are individually iff they have drop glue. This means that any captured lifetimes within a bound generic are known to only be touched at drop timing by that generic type's own drop glue, and in the absense of that, the region is not required to be live at drop timing. This is NLL for user-defined types without the unsafe `#[may_dangle]` attribute.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [September 16, 2024, 9:37pm UTC](https://internals.rust-lang.org/t/drop-bounds-are-useless-so-lets-change-that/21044/6 "2024-09-16T21:37:12Z")

</div>

Closed on September 16, 2024, 9:37pm UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
