Is Drop implicitly included within all Traits (ie. within fat-pointer vtables)?


#1

So my impression is that a trait fat pointer results in type-erasure and cannot be cast back to (a pointer of) the original object type. Specifically, all accesses to the object within the trait would need to be done through methods in the trait vtable pointed to in the fat pointer. This understanding of mine was partially solidified by my implementation of a similar concept in nim (previously nimrod) a bit back.

But then, it occurred to me that due to this type erasure, it’s not obvious how one can drop the object referenced by the trait fat pointer, since the vtable would need a reference to drop(). But it works fine in rust as demonstrated below,

struct HasDrop;

impl Drop for HasDrop {
    fn drop(&mut self) {
        println!("Dropping!");
    }
}

trait TraitThatDoesNotRequireDrop {}
impl TraitThatDoesNotRequireDrop for HasDrop {}

// [] moved into here to get Dropped, but Boxed trait doesn't indicate presence of drop().
fn foo(item: Box<[Box<TraitThatDoesNotRequireDrop>]>) {
    println!("In foo!");

    // But drop() runs successfully on foo completion.
}

fn main() {
    let x : Box<[Box<TraitThatDoesNotRequireDrop>]> = Box::new([Box::new(HasDrop)]);
    println!("HasDrop box created");
    foo(x);
    println!("Last statement in main");
}

The output is

HasDrop box created
In foo
Dropping!
Last statement in main

Does that mean that Drop is included in the vtable of all traits (effectively being implemented for all types and never being required within the type constraints)? If so, is there any other trait that has that property?


#2

Why is all the code showing up in 1-line on the post? The preview showed the correct multi-line formatting.


#3

Discourse seems to have a quirk or bug if a code block is not preceded by a completely blank line. I edited the comment to fix the formatting.


#4

We definitely include type-but-not-the-trait-specific info in all the vtables. The only 3 I know about are:

  • drop glue
  • size
  • alignment

Drop glue is slightly different from the actual Drop trait in that every type has drop glue, but only some types implement Drop. Drop glue cares about the following things:

  • I implement Drop
  • I contain types that implement Drop

As pesudocode, it’s roughly:

# NOTE: basically none of this is legal to do in normal Rust
drop_glue<T>(target: &mut T):
    if T is Drop then:
        target.drop()
    for field in target.fields():
        drop_glue(field)

If a type doesn’t have or contain destructors, then this should be a no-op.


#5

GTK! Thanks, both!