# Fixing the \`Drop\` trait bound

**URL:** https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458
**Category:** language design
**Created:** [February 16, 2019, 4:00pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458 "2019-02-16T16:00:28Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![CodesInChaos](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/codesinchaos/32/5211_2.png) [@CodesInChaos](https://internals.rust-lang.org/u/CodesInChaos)
#### Post date: [February 16, 2019, 4:00pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/1 "2019-02-16T16:00:28Z")

</div>

**What would I intuitively expect the `Drop` trait to behave like:**

When copying values, there are three categories of types:

1. Cannot be copied (no traits)
2. Can be copied using user-defined code (implements `Clone`)
3. Can be trivially copied with mem-copy (implements `Copy` which derives from `Clone`)

For dropping the situation should be similar, with the equivalent categories:

1. Cannot be dropped (linear types). Those currently do not exist in Rust.
2. Can be dropped using user-defined code (implements `Drop`).
3. Can be trivially dropped without executing any user-defined code (implements a hypothetical `TrivialDrop` trait which derives from `Drop`)

This way additional traits represent additional constraints on the type.

**What is the current behaviour?**

- `Drop` is implemented only by types which manually implement it. For example `Vec<T>` [implements `Drop`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0a2fa7f9ad04ecc0f3a80a023b4b8914)
- `Drop` is [not implemented](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b3cef9460a6625dab40057dd024e327a) for trivially droppable types (like `&mut T` and all `Copy` types)
- `Drop` is [not implemented](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10d559d39b4369d0ff6a2c794fa088e0) for types which run user defined code when getting dropped, but where the drop code is associated with a contained field and not the type itself. (e.g. `(Vec<i32>, )` and `String`)

**The current design has a number of issues:**

- `Drop` is effectively a negative trait bound

- `Drop` and `Copy` are effectively exclusive traits

- Using `Drop` as bound is useless, because it doesn't add any capabilities (every type can be dropped).

- Moving ownership of a resource from a public type to a private holder-type stored in a field is a breaking-change. This should be an implementation detail. For example `Vec<u8>` is `Drop` but the nearly equivalent `String` is not.

- It's un-intuitive and invites mistakes

**I propose fixing this over several steps:**

1. Do a crater run to find all known usages of `Drop` as bound/parent trait. Check if any of them have a good reason (I can't think of one).

2. Add a compiler warning whenever `Drop` is used as trait bound or when another trait is derived from `Drop`. ([Older proposal](https://internals.rust-lang.org/t/can-we-deprecate-t-drop-trait-bounds/9238))

3. Fix the `Drop` trait a) have the `Drop` bound match all types This is technically a [breaking change](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d69b3a6060d08e37b8ba3500d7beb203), but I expect breakage to be extremely rare. It might even be acceptable to do this outside an edition change. b) turn the warning into an error (obviously a breaking change)

4. Introduce a `TrivialDrop` trait as an analog to `Copy`. This step will likely happen much later, or not at all if the benefits do not outweigh the costs.

**How would `TrivialDrop` work?**

- `Copy` derives from `TrivialDrop`
- The compiler automatically derives `TrivialDrop` for all types which don't implement `Drop` manually and all of whose field are `TrivialDrop` (similar to `Sync`/`Send`). Since implementing an empty `Drop` is already an opt-out, we don't need `impl !TrivialDrop for T {}`.
- This could allow generic functions to take advantage of non lexical lifetimes, since the call to `drop` at the end of the scope isn't required if `T` implements `TrivialDrop`.

A [Similar proposal](https://github.com/rust-lang/rfcs/pull/2534#discussion_r244580008), which also adds a `Destruct` trait in addition to `Forget`/`TrivialDrop`. That trait doesn't seem very useful to me, but could be added later if desired.

**If linear types are ever added:**

- All normal types would implement `Drop`, linear types would not
- `Drop` would be a default trait bound which can be opted out of using `?Drop`. Just like `Sized` works today.
- `TrivialDrop` would derive from `Drop`.
- Traits would not automatically derive from `Drop`.

I don't expect linear types to ever be integrated into Rust, but them being nicely compatible with this design is a good sign.

---

<div class="post-metadata">

### Author: ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)
#### Post date: [February 16, 2019, 4:16pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/2 "2019-02-16T16:16:28Z")

</div>

> [@CodesInChaos](#):
>
> Cannot be dropped (linear types)

From what I can tell, truly linear types would need to be explicitly/manually dropped; they are not un-droppable. (However, this can be a matter of definition.)

> [@CodesInChaos](#):
>
> `Copy` derives from `TrivialDrop`

Wouldn't this be a wildly breaking change? Also, if you deem `Drop` useless, why would you want a `TrivialDrop` trait that means even less?

> [@CodesInChaos](#):
>
> It’s un-intuitive and invites mistakes

My first reaction to this was "citation needed". I literally couldn't imagine anything more intuitive for defining custom destruction code than a single trait with a single method that is automatically run when destruction happens. I don't see how that is considered unintuitive.

> [@CodesInChaos](#):
>
> Moving ownership of a resource from a public type to a private holder-type stored in a field is a breaking-change. This should be an implementation detail. For example `Vec<u8>` is `Drop` but the nearly equivalent `String` is not.

If you never want to place `Drop` as a trait bound, or if you even want to _stop_ people from using `Drop` as a trait bound, this shouldn't be an issue either. I also don't think it's purely an implementation detail: it's entirely reasonable that modifying a public type changes its public API.

All in all, this proposal introduces a lot of complexity and breakage, and its motivation is really weak, so I don't see a good reason for adding either.

---

<div class="post-metadata">

### Author: ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)
#### Post date: [February 16, 2019, 6:17pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/3 "2019-02-16T18:17:16Z")

</div>

> [@H2CO3](#):
>
> Also, if you deem `Drop` useless, why would you want a `TrivialDrop` trait that means even less?

`TrivialDrop`, as it is named here, does mean something: it means the type and all its transitive constituents have no custom disposal logic, and to destroy a value of the type it suffices to deallocate its backing memory with no other actions performed. This property is currently impossible to express in the trait system except indirectly, through a `Copy` bound, but that has other consequences that may not always be desirable. OP links to a comment of mine under an RFC where having a way to express this property was quite crucial.

The same functionality was proposed by myself in the above-mentioned comment under the name `Forget`, and earlier by @canndrew [under the same name](https://internals.rust-lang.org/t/pre-pre-rfc-forget-trait/5606); @glaebhoerl also [sketched the basic idea on Reddit](https://www.reddit.com/r/rust/comments/4l4wjl/a_report_on_regions_and_linear_types/d3lygjn/), with the name `DropIsForget`. I think it's telling that multiple people stumbled upon this idea independently.

---

<div class="post-metadata">

### Author: ![CodesInChaos](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/codesinchaos/32/5211_2.png) [@CodesInChaos](https://internals.rust-lang.org/u/CodesInChaos)
#### Post date: [February 16, 2019, 7:32pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/4 "2019-02-16T19:32:54Z")

</div>

> [@H2CO3](#):
>
> From what I can tell, truly linear types would need to be explicitly/manually dropped; they are not un-droppable. (However, this can be a matter of definition.)

For the purpose of this post, a droppable type can be assigned to `_` or go out of scope without causing a compiler error.

> [@H2CO3](#):
>
> > `Copy` derives from `TrivialDrop`
> 
> Wouldn’t this be a wildly breaking change?

Since `Copy` already requires the absence of a destructor and `TrivialDrop` is proposed as an auto-implemented trait, this shouldn't be a breaking change. It just splits out part of the requirements of `Copy` into a separate trait.

> [@H2CO3](#):
>
> My first reaction to this was “citation needed”.

I would never have expected the difference between user implemented `Drop` and a compiler implemented one that calls the user-implement `Drop` of the type's fields to affect which traits a type implements.

For a fun exercise, try to predict which standard collections fulfill the `Drop` bound, and which do not.

> [@H2CO3](#):
>
> I also don’t think it’s purely an implementation detail: it’s entirely reasonable that modifying a public type changes its public API.

I think refactoring a type to move its resource ownershop into an inner holder type should not be a breaking change. So

```
struct Collection{
    
}

impl Drop for Collection
{
    fn drop(&mut self)
    {
        
    }
}

```

could be turned into:

```
impl Drop for Inner
{
    fn drop(&mut self)
    {
        
    }
}

struct Collection{
    inner:Inner
}

```

without breaking consumers.

> [@H2CO3](#):
>
> this proposal introduces a lot of [...] breakage

Disallowing `Drop` as trait bound introduces breakage, but only in cases that no sane code will hit.

Introducing `TrivialDrop` should not result in any breakage.

> [@H2CO3](#):
>
> this proposal introduces a lot of complexity and breakage

Introducing `TrivialDrop` shouldn't be too complex, since deriving `Copy` already requires the same logic. And it might even reduce the complexity of NLL slightly, since it could take advantage of already implemented traits, instead of duplicating the logic.

---

<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: [February 16, 2019, 8:10pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/5 "2019-02-16T20:10:26Z")

</div>

I say we should start with [a lint for bounding by Drop](https://github.com/rust-lang/rust-clippy/issues/3773), then add TriviallyDrop as a separate RFC.

---

<div class="post-metadata">

### Author: ![CodesInChaos](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/codesinchaos/32/5211_2.png) [@CodesInChaos](https://internals.rust-lang.org/u/CodesInChaos)
#### Post date: [February 16, 2019, 8:22pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/6 "2019-02-16T20:22:03Z")

</div>

Considering how broken `Drop` bounds are, I’d prefer a compiler warning which gets turned into an error in the next edition upgrade over a clippy lint.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [February 16, 2019, 10:19pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/7 "2019-02-16T22:19:32Z")

</div>

> [@CodesInChaos](#):
>
> This could allow generic functions to take advantage of non lexical lifetimes, since the call to `drop` at the end of the scope isn’t required if `T` implements `TrivialDrop` .

Could this be replaced by non-trivial `EagerDrop` instead?

I'd like to be able to state that a `drop` implementation isn't sensitive to ordering and doesn't have side effects (like locks do). I presume that would also allow NLL for eagerly-droppable types.

---

<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: [February 17, 2019, 5:02am UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/8 "2019-02-17T05:02:02Z")

</div>

> [@CodesInChaos](#):
>
> Considering how broken `Drop` bounds are, I’d prefer a compiler warning which gets turned into an error in the next edition upgrade over a clippy lint.

I agree that this should eventually become a compiler lint, but starting out in Clippy means [I was able to implement it](https://github.com/rust-lang/rust-clippy/pull/3776) without having to screw with multiple-hour-long compiler compiles.

I'll move it into rustc itself once it's been tested in clippy.

---

<div class="post-metadata">

### Author: ![ExpHP](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/exphp/32/6208_2.png) [@ExpHP](https://internals.rust-lang.org/u/ExpHP)
#### Post date: [February 17, 2019, 9:12pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/9 "2019-02-17T21:12:47Z")

</div>

See related discussion on RFC #2632 about `Drop` bounds in const functions. It's looking like that feature may need some sort of `ConstDrop` trait (`TrivialDrop`):

> <https://github.com/rust-lang/rfcs/pull/2632>
>
> TLDR: Allow
> 
> \`\`\`rust
> const fn add\<T: Add\>(a: T, b: T) -\> T::Output {
> a +… b
> }
> \`\`\`
> 
> and
> 
> \`\`\`rust
> pub struct Foo\<T: Trait\>(T);
> impl\<T: ?const Trait\> Foo\<T\> {
> fn new(t: T) -\> Self {
> // not calling methods on \`t\`, so we opt out of requiring
> // \`\<T as Trait\>\` to have const methods via \`?const\`
> Self(t)
> }
> }
> \`\`\`
> 
> cc @Centril @varkor @RalfJung @eddyb 
> 
> This RFC has gone through an extensive pre-RFC phase in https://github.com/rust-rfcs/const-eval/pull/8
> 
> \[Rendered\](https://github.com/oli-obk/rfcs/blob/const\_generic\_const\_fn\_bounds/text/0000-const-generic-const-fn-bounds.md)
> 
> Triage: pFCP comment: https://github.com/rust-lang/rfcs/pull/2632#issuecomment-481395091

* * *

**Edit:** Based on discussion within the last 24h it looks like `ConstDrop` isn't good enough either. See this amusing snippet:

> > if we start requiring `T: ConstDrop` then this will presumably creep into the APIs of the standard library and cause breakages so it may not be feasible?
> 
> Oh right, this doesn't do the effect systemy trickery where the bound loses its constness.
> 
> Actually. we can do this. We'll make `const ConstDrop` be implemented for the types as the rules say in this RFC, but we also implement `ConstDrop` (without the `impl const ConstDrop`, just `impl ConstDrop`) for _all_ types

---

<div class="post-metadata">

### Author: ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)
#### Post date: [April 14, 2019, 8:37pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/10 "2019-04-14T20:37:46Z")

</div>

`ConstDrop` is not exactly this proposal, although it addresses a similar sub-problem of expressing ‘all transitively constituent `Drop` implementations have property X’. But I think overhauling `Drop` as proposed here could make the other proposal simpler as well: `const ConstDrop` would simply become `const Drop`, with the following additional rules:

- `TrivialDrop`/`Forget` implies `const Drop`.
- Automatically-generated inductive/destructuring impls of `Drop` are `const` whenever all constituents’ impls are `const`

---

<div class="post-metadata">

### Author: ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)
#### Post date: [April 15, 2019, 10:38am UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/11 "2019-04-15T10:38:57Z")

</div>

[The RFC](https://github.com/rust-lang/rfcs/pull/2632) was actually recently changed to propose basically what the OP proposed: `T: Drop` as a bound changes nothing (because all types can be dropped), and `T: const Drop` (well, really `const fn foo<T: Drop>`) means “can be dropped in const code”, which basically means that (recursively) all involved `Drop` instances must be `impl const Drop`.

Cc @oli-obk

---

<div class="post-metadata">

### Author: ![crlf0710](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/crlf0710/32/1987_2.png) [@crlf0710](https://internals.rust-lang.org/u/crlf0710)
#### Post date: [April 16, 2019, 7:14am UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/12 "2019-04-16T07:14:59Z")

</div>

@RalfJung That’s a little strange… If `Drop` actually means “running destructor”, then the existing `Drop::drop` is actually `before_drop` or something…

---

<div class="post-metadata">

### Author: ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)
#### Post date: [April 16, 2019, 8:51am UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/13 "2019-04-16T08:51:21Z")

</div>

It’s a little strange, yes – but it’s not the only way in which `Drop` is magic. Like, you cannot even call `Drop::drop` (in safe code at least).

---

<div class="post-metadata">

### Author: ![gnzlbg](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gnzlbg/32/3858_2.png) [@gnzlbg](https://internals.rust-lang.org/u/gnzlbg)
#### Post date: [April 16, 2019, 12:40pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/14 "2019-04-16T12:40:33Z")

</div>

> [@CodesInChaos](#):
>
> Cannot be dropped (linear types). Those currently do not exist in Rust.

The more I use `ManuallyDrop` the more I wish there was a static analysis that detects that I've forgotten to properly drop these in all code paths.

---

<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: [July 15, 2019, 12:40pm UTC](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458/15 "2019-07-15T12:40:33Z")

</div>

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