# Pre-pre-RFC: \`NoDrop\` marker trait

**URL:** <https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682>\
**Category:** language design\
**Created:** [November 28, 2021, 5:09pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682 "2021-11-28T17:09:18Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [November 28, 2021, 5:09pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/1 "2021-11-28T17:09:18Z")

</div>

Here's an idea that I've wondered about:

## Motivation

In some APIs, it would be nice to force the API user to call some method rather than allowing drop glue to clean up the type. For example, if some work that needs to be done around drop time is fallible and you'd like to force the caller to handle it.

I've personally wanted this for Quinn, where I'd like to "force" the API user to deal with some potentially required follow-up when the caller is [done](https://github.com/quinn-rs/quinn/blob/main/quinn-proto/src/connection/streams/recv.rs#L283) consuming from a receive stream.

## Guide-level explanation

core will provide a `NoDrop` marker trait. If it is implemented for a type, owned values of that type may not go implicitly out of scope. The only way to clean them up would be to destruct the values. Typically this would be done by a method of the type, which allows the API to for example handle fallibility, or that method could be made `async`.

## Drawbacks

Since explicitly cleaning up is less ergonomic than implicit drop glue, we would not want this trait to be widely used. However, it seems like a useful increase of expressivity.

## Alternatives

This seems related to [`ManuallyDrop`](https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html), which I first encountered when implementing the aforementioned Quinn code. `ManuallyDrop`, as I understand it, prevents the compiler from inserting drop glue, but doesn't provide any guarantees that cleaning up is done in some other way. By contrast, `NoDrop` would force a caller to still implement some way of cleaning up a type's resources, which seems like an improvement to me.

---

<div class="post-metadata">

**Author:** ![bluss](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bluss/32/2264_2.png) [@bluss](https://internals.rust-lang.org/u/bluss)\
**Post date:** [November 28, 2021, 5:17pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/2 "2021-11-28T17:17:22Z")

</div>

This must have been discussed before - I would look for linear types as the keyword. Just saying this so that the discussion can start from the conclusions of previous iterations.

How is panic/unwinding handled in this plan, since normally everything is dropped in that case too?

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [November 28, 2021, 5:28pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/3 "2021-11-28T17:28:37Z")

</div>

Unwinding is a good point. I can think of two approaches:

- `NoDrop` only forces non-panicking code to handle cleanup explicitly. This is easier but might be problematic in that it doesn't force the API user to think about this case as much, and so they might think that dropping will not handle when it actually might.
- The `NoDrop` adds a `panicked(self)` method where the impl is forced to handle that case explicitly. This forces the implementer to consider unwinding, which seems like an improvement, and the considerations that the API implementer wants to force on the caller might not be as important in the panic case. For example, Quinn currently [implements `Drop`](https://github.com/quinn-rs/quinn/blob/main/quinn-proto/src/connection/streams/recv.rs#L329) for `Chunks` and would use that implementation in `NoDrop::panicked()` rather than `Drop` if this was added.

---

<div class="post-metadata">

**Author:** ![mathstuf](https://avatars.discourse-cdn.com/v4/letter/m/958977/32.png) [@mathstuf](https://internals.rust-lang.org/u/mathstuf)\
**Post date:** [November 28, 2021, 5:44pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/4 "2021-11-28T17:44:33Z")

</div>

Additionally, what is done for [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html) on a value of such a type? Note this bit in those docs which references the limitations of Rust's safety guarantees:

> Because forgetting a value is allowed, any `unsafe` code you write must allow for this possibility. You cannot return a value and expect that the caller will necessarily run the value’s destructor.

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [November 28, 2021, 5:57pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/5 "2021-11-28T17:57:33Z")

</div>

I would expect `mem::forget()` to do the same things as it does on other values. Since its purpose is to bypass drop glue, it seems sensible that forbidding drop glue to run has no effect on it.

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [November 28, 2021, 7:14pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/6 "2021-11-28T19:14:17Z")

</div>

Couldn't you just panic on drop as a way to force the user to convert to a different type (and thereby force some final processing?)

---

<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:** [November 29, 2021, 12:25am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/7 "2021-11-29T00:25:55Z")

</div>

> [@skysch](#):
>
> Couldn't you just panic on drop as a way to force the user to convert to a different type

That makes it a runtime property; the value of "true" linear types is making this a compile time reminder.

Actually, though, that may be a good way to handle pseudo-linear types in Rust: do it as a stronger form of `#[must_use]` which warns if the value is implicitly dropped at end of scope.

* * *

> [@djc](#):
>
> In some APIs, it would be nice to force the API user to call some method rather than allowing drop glue to clean up the type.

The main problem with linear types is that they don't compose well. How do you handle containers of linear types? In a way it's somewhat similar to pinning; a pinned type in C++ parlance is one with the move constructor (and probably the copy constructor) deleted; a linear type is one with the destructor deleted (or private, I suppose).

In C++, containers "support" exotic types without destructors by just duck typed APIs, and getting template errors when you try to use an API that does something the contained type doesn't support. Rust doesn't have that "luxury," in that it avoids post-monomorohization errors, and should enable/disable non-linear-compatible APIs via generics.

In a best case world, we make every `for<T>` bound by `Drop` by default (the way they are `Sized`, and in this world `T : Drop` means droppable, not implements `Drop` explicitly), and every generic needs to consider if it can opt in to `?Drop` types.

This is, at best, a lot of work across the entire ecosystem to support a small family of exotic types. And there are other issues with new `?Trait` bounds.

The best reference article for the pains of actually linear types is probably Gankra's:

> **[The Pain Of Linear Types In Rust - Faultlore](https://faultlore.com/blah/linear-rust/)**

And it's also worth noting that a linear type feature is _very_ likely to try to get used for soundness, as in the type must be `drop`ped, and cannot be forgotten. Whether a design could actually guarantee that is an open question with a likely negative answer, that forgetting to run destructors is always sound for safe code to do.

---

<div class="post-metadata">

**Author:** ![pdolezal](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pdolezal/32/7137_2.png) [@pdolezal](https://internals.rust-lang.org/u/pdolezal)\
**Post date:** [November 29, 2021, 7:12am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/8 "2021-11-29T07:12:52Z")

</div>

While I see some appeal in your proposal, as @CAD97 noted, it brings a lot of issues. However, it reminds me a bit of my [own question](https://internals.rust-lang.org/t/question-closing-resources-like-std-write/13169/8) and the related idea of a `Close` trait. Perhaps, it might be worth extending this idea with a hook that could be used during `panic`king as you suggested, with the default implementation simply `drop`ping the thing.

---

<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:** [November 29, 2021, 11:24am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/9 "2021-11-29T11:24:13Z")

</div>

Random thought: would it be possible to apply the [`no-panic` trick](https://lib.rs/crates/no-panic) to the drop impl of a type to check whether its drop glue is ever run?

This of course has all the same limitations of no-panic, due to being an error at the linker stage post optimization and codegen.

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [November 29, 2021, 11:52am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/10 "2021-11-29T11:52:44Z")

</div>

An additional limitation of using the no-panic trick on a drop impl would be that it becomes impossible to turn the value into a trait object as the vtable always references the drop glue.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [November 29, 2021, 12:05pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/11 "2021-11-29T12:05:29Z")

</div>

I know most of you are aware of this, but I'll post it nonetheless for reference, especially since it's an implementation which already offers a possible answer to most of the questions regarding panic and whatnot.

With "branding type constructors" (type constructors which manage to yield an instance of a unique type per instantiation), one can use the type-state pattern to ensure that a non-diverging input has properly called the desired destructor (or constructor! This can be used to structurally prove that an out-pointer to a structure is initialized by initializing each and every field).

The root point of a brand, in Rust, is an "anonymous" / unnameable lifetime, either because macro-generated, or because higher-order (callback style):

```rust
mod lib {
    type PhantomInvariant<'lt> =
        ::core::marker::PhantomData<fn(&'lt ()) -> &'lt ()>
    ;

    pub
    struct Id<'brand> /* = */ (
        PhantomInvariant<'brand>,
    );

    pub
    struct Foo<'brand> {
        // …
        _brand: Id<'brand>,
    }

    impl<'brand> Foo<'brand> {
        pub
        fn consume (self: Foo<'brand>)
          -> ProofOfConsumption<'brand>
        {
            // …
            ProofOfConsumption(self._brand)
        }
    }
    // where
    pub
    struct ProofOfConsumption<'brand>(Id<'brand>);

    /// Here is where the magic happens: "must consume"-yielding constructor!
    impl Foo<'_> {
        pub(self) // private
        fn new (/* … */)
          -> Self
        {
            Self {
                // …
                _brand: Id(<_>::default()),
            }
        }

        pub
        fn with_new<R> (
            /* …, */
            scope: impl for<'brand> FnOnce(Foo<'brand>) -> (ProofOfConsumption<'brand>, R),
        ) -> R
        {
            scope(Self::new(/* … */)).1
        }
    }
}

```

The key is thus that

```rust
for</* any */ 'brand> …(Foo<'brand>) -> ProofOfConsumption<'brand> …

```

required callback: it needs to be a closure whose function body is lifetime-agnostic (otherwise it wouldn't meet the desired higher-order signature), and while doing so, it needs, for each / any input `'brand` in `Foo`, to be able to yield a `ProofOfConsumption<'brand>`.

This will only be possible if the logic of the given scope / callback, for every possible branch inside it, either `.consume()`s `Foo()`, or diverges.

- [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=871cb6e0a20cef94b87e51637dc521db)

Then, one could always write a `Drop` impl of `Foo<'_>`, which would only be called in the `panic!`-king case: `impl Drop for Foo<'_>` and `impl Foo<'_> { fn consume(self)` would thus be the two function bodies handling both cases, with `consume` being able to fail or whatnot, but the `Drop` not (and one could even `panic!` within the `Drop` impl to abort if the `scope` panics, should that `consume` call be otherwise mandatory).

* * *

This does have the issue of requiring callbacks, hence not playing super nicely with `try` blocks, `async`, or early `break`, `continue`, or `return`s.

* * *

This also answers the question of structural combination of such entities, or even that of storing those in a collection: the unique lifetime for each makes the latter impossible (😬), and the former possible but quite cumbersome:

```rust
struct Baz<'foo, 'bar>(Foo<'foo>, Bar<'bar>);

impl Baz {
    fn with_new<R> (
        scope: impl for<'f, 'b> FnOnce(Baz<'f, 'b>) -> (ConsumedBaz<'f, 'b>, R),
    ) -> R
    {
        Foo::with_new(|foo| {
            Bar::with_new(|bar| {
                let baz = Baz(foo, bar);
                let (baz_token, ret) = scope(baz);
                let (foo_token, bar_token) = baz_token.into_parts();
                (bar_token, (foo_token, ret))
            })
        })
    }
}

```

😅

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [November 29, 2021, 12:55pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/12 "2021-11-29T12:55:23Z")

</div>

> [@CAD97](#):
>
> Random thought: would it be possible to apply the [`no-panic` trick](https://lib.rs/crates/no-panic) to the drop impl of a type to check whether its drop glue is ever run?
> 
> This of course has all the same limitations of no-panic, due to being an error at the linker stage post optimization and codegen.

no-panic is not correct rust code, and is not even well-formed. It relies on DCE to avoid errors, which is not reliable as it is an optional optimization. The way that I would describe it is Ill-formed, No Diagnostic Required. I would actively discourage attempts to extend this to a generalized no-drop operation

---

<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:** [November 29, 2021, 1:23pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/13 "2021-11-29T13:23:01Z")

</div>

> [@CAD97](#):
>
> In a best case world, we make every `for<T>` bound by `Drop` by default (the way they are `Sized` , and in this world `T : Drop` means droppable, not implements `Drop` explicitly), and every generic needs to consider if it can opt in to `?Drop` types.

This was discussed before:

> [@Fixing the \`Drop\` trait bound](https://internals.rust-lang.org/t/fixing-the-drop-trait-bound/9458):
>
> What would I intuitively expect the Drop trait to behave like: When copying values, there are three categories of types: Cannot be copied (no traits) Can be copied using used-defined code (implements Clone) Can be trivially copied with mem-copy (implements Copy which derives from Clone) For dropping the situation should be similar, with the equivalent categories: Cannot be dropped (linear types). Those currently do not exist in Rust. Can be dropped using user-defined code (implements Drop)…

---

<div class="post-metadata">

**Author:** ![aidancully](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aidancully/32/5532_2.png) [@aidancully](https://internals.rust-lang.org/u/aidancully)\
**Post date:** [November 30, 2021, 8:46pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/14 "2021-11-30T20:46:40Z")

</div>

> [@CAD97](#):
>
> In a best case world, we make every `for<T>` bound by `Drop` by default (the way they are `Sized` , and in this world `T : Drop` means droppable, not implements `Drop` explicitly), and every generic needs to consider if it can opt in to `?Drop` types.
> 
> This is, at best, a lot of work across the entire ecosystem to support a small family of exotic types. And there are other issues with new `?Trait` bounds.

When I looked at this years ago, I suggested this effort could be significantly helped by a [lint](https://github.com/aidancully/rfcs/blob/linear-trait/text/0000-linear-type.md#the-explicit_bounds-lint). I think automated refactoring may also be possible. It would likely be very irritating across the ecosystem, as folks who maintain generic container crates may be "encouraged" to make their crates "linear"-aware... On the other hand, I don't believe the use cases are as exotic as you suggest. Linear types seem (to me) to potentially make async future cancellation much more intentional (no longer a foot-gun), which is very relevant to how Rust seems to be evolving today.

---

<div class="post-metadata">

**Author:** ![aidancully](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aidancully/32/5532_2.png) [@aidancully](https://internals.rust-lang.org/u/aidancully)\
**Post date:** [December 12, 2021, 5:36am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/15 "2021-12-12T05:36:23Z")

</div>

For whatever it's worth (and apologies if I'm stealing anyone's thunder), I just posted something like an updated proposal here: [Communicating With Code: Less Painful Linear Types](http://aidancully.blogspot.com/2021/12/less-painful-linear-types.html)

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [December 12, 2021, 11:00am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/16 "2021-12-12T11:00:32Z")

</div>

Wouldn't the escape hatch described in the article nullify any use of linear types for safely exposing unsafe interfaces (like scoped threads/tasks)?

---

<div class="post-metadata">

**Author:** ![tema2](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tema2/32/6498_2.png) [@tema2](https://internals.rust-lang.org/u/tema2)\
**Post date:** [December 12, 2021, 7:32pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/17 "2021-12-12T19:32:11Z")

</div>

I had idea of creating `#[finalize]` methods: ones that require `!ImplicitDrop`, take `self` and are required to be called for a value to be disposed. As the least possible disposing method `Drop::drop` (+ drop glue) has been considered. Inside of `finalize` methods _all_ types may be destructured.

Pro: `finalize` methods can have arguments and return types.  
Con: requires adding support on per-item basis.\

Just to share.

I also encourage `drop_unwind` proposal from Niko, which he wrote somewhere. I'd be nice addition to handle unwind in context of such proposals.

---

<div class="post-metadata">

**Author:** ![aidancully](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aidancully/32/5532_2.png) [@aidancully](https://internals.rust-lang.org/u/aidancully)\
**Post date:** [December 24, 2021, 8:28pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/18 "2021-12-24T20:28:17Z")

</div>

No, in order to use the escape hatch, the `Drop` implementation needs to honor the linear type constraints. `consume` takes the value by move, and it's not allowed to fall out of scope in the trait implementation. This means you're more-or-less forced to forward the value to a consuming function defined against the internal value. You're still forced to honor the type author's intent when you adapt the type to be affine.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [December 24, 2021, 9:17pm UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/19 "2021-12-24T21:17:50Z")

</div>

> [@aidancully](#):
>
> No, in order to use the escape hatch, the `Drop` implementation needs to honor the linear type constraints.

But `Drop::drop` takes an `&mut self`, so there's no instance of your linear type falling out of scope. Moreover you can `std::mem::forget` the wrapper, thus the linear type can't really rely on it being linear for safety.

---

<div class="post-metadata">

**Author:** ![earthengine](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/earthengine/32/2577_2.png) [@earthengine](https://internals.rust-lang.org/u/earthengine)\
**Post date:** [December 25, 2021, 4:17am UTC](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/20 "2021-12-25T04:17:38Z")

</div>

I have an alternative solution

> [@Christmas proposal: "Don't call" annotation](https://internals.rust-lang.org/t/christmas-proposal-dont-call-annotation/15851):
>
> Marry Christmas to you all. There are two different motivations lead to this proposal. One is in this existing proposal: [https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/20](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682/20) Me, and the author of the post above wanted to have a way to say "this object cannot be dropped unless you have done the check that it is a good time to drop it". My precise challenge is that the object may have active reference leaked so it may not in a safe status to be dropped. To write unsafe co…

With this we don't use a marker trait, instead we have a special annotation, so we can write our own marker object (line `PhantomPinned`).

```rust
struct PhantomNoDrop;
impl Drop for PhantomNoDrop {
    #[do_not_call]
    fn drop(&mut self) { unreachable!() }
}

```

With this any type that in its generated drop clue cannot call this `drop` implementation otherwise the compiler will complain.

[Next page](https://internals.rust-lang.org/t/pre-pre-rfc-nodrop-marker-trait/15682.md?page=2)
