Destructorless types


I recently run into the somewhat obnoxious fact that if T: Drop, then you can’t move out of public fields of T (for obvious reasons). In the presence of a C++ ptr-to-member type this actually becomes really annoying, because there’s no way to write a function that takes a generic T, a field offset over it, and uses it to move out of the T with it:

// using syntax from my other post
fn move_out_of<T, U>(x: T, offset: T.U) -> U { 
    // drop glue here
// .. imagine we're in liballoc
let x: Unique<i32> = move_out_of(Box::new(0), offsetof Box::<_>.0); // unsound!

It’s unclear if this is a problem, since we could, in principle, make T.U unilaterally uninhabited if T: Drop… but then we’re not allowed to do write this function, which is always sound:

fn ref_out_of<T, U>(x: &T, offset: T.U) -> &U {
    // drop glue? what drop glue?

I think this problem can be solved completely without touching the compiler:

// in core::marker or whatever
pub unsafe auto trait NoDrop {}
impl<T: Drop> !NoDrop for T {}

I’m pretty sure implementing this trait manually is always UB. Of course, this trait needs to be turned into a lang item for the purposes of generic move, so you can write

fn move_out_of<T: NoDrop, U>(x: T, offset: T.U) -> U { 


There’s not generally support for negative trait bounds, but one thing you can rely on is that Copy and Drop are exclusive. Of course, this still leaves many types that are neither.


Agreed- Niko’s chalk blogposts convinced me that negative reasoning is a pretty bad idea, and special casing T: !Drop is… gross, much like T: ?Sized is. I’m proposing the addition of an auto trait, for which de-implementations are allowed.

A lot of this comes out of the realization that “has trivial destructor” is the sort of assertion you’d like to be able to use, but because of how destructors were implemented we’re kind of stuck in this hole where we need to add a negation trait. Afaik, though, the compiler will accept my definition of NoDrop just fine.