Updated proposal
Due to your feedback, the original proposal needs to be changed. Originally, I stated that Leave
is a super-trait of Drop
. This meant that no destructor was available for a !Leave
type, even in the panic case, which in turn makes unwinding hard to do.
Additionally, I realize I was trying to solve two related, but separate, problems:
- Some state machine types (like certain
Future
s) should not be arbitrarily dropped under normal non-panic control flow. Instead, they should be driven to their terminal state. - Types that cannot leak, i.e. live past the lifetime that rustc expects them to have.
Both of these could be auto-traits, and considered independently. These traits could be merged to a single trait which upholds the guarantees of both, but for the sake of discussion and separation of concerns I present them separately.
ImplicitDrop auto-trait
An !ImplicitDrop
instance cannot be dropped regularly by going out of scope or through std::mem::drop(..)
. This forces the user to destruct the instance gracefully on all code paths, or optionally force-destroying the value.
Purpose: Prevent accidentally breaking invariants of state machine types.
How to destruct an !ImplicitDrop
type:
-
Graceful: Destructure the type (typically through a
fn foo(self, ..)
function). -
Forceful: Add an "explicit/force drop", e.g.
std::mem::destroy<T: ?ImplicitDrop>(t: T)
function:-
destroy
invokes drop as usual but suppresses the error. -
destroy
is safe, but should be considered potentially invariant breaking. It can be invoked by a caller that has driven the instance to its terminal state (e.g.when an iterator is consumed or a future is ready) -
destroy
is invoked on the panic-unwind path, just like a regular drop.
-
Use cases: Futures, iterators and custom state machines that should be driven to completion before dropped can force the user to consider all exit points. Generic containers offer ?ImplicitDrop
impls if they can uphold this contract transitively.
Alternatives: There's some wiggle room for considering a lint instead of an error here. The problem with lints is transitivity in nested types and so on.
UPDATE: I was not aware that destructuring a type currently invokes Drop
. (Thanks @mbrubeck for pointing it out). We need to find a better definition of "accidentally permaturely dropping".
Leak auto-trait
Very simple: it is unsafe to leak a !Leak
type. As such, not implemented by Arc
and Rc
, and other generic containers that can leak.
Leaking is defined as outliving the input lifetime.
Immediate use case: scoped threads and async tasks.