Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are vtables generated for all types that implement a trait?

If I have a trait Foo, and some implementors Bar, Baz.

impl Foo for Bar {
}
impl Foo for Baz {
}

But say I only use one of them ever as a trait object,

let bar = Bar {..};
let foo: &dyn Foo = &bar;

Then will my binary still have vtable for both? Does this behaviour change between debug and release builds?

like image 432
zombiesauce Avatar asked Oct 14 '21 16:10

zombiesauce


People also ask

Does rust use Vtables?

Vtables in Rust Like C++, dynamic dispatch is achieved in Rust though a table of function pointers (described here in the rust docs).

What is dynamic dispatch Rust?

Dynamic dispatch. Rust provides dynamic dispatch through a feature called 'trait objects'. Trait objects, like &Foo or Box<Foo> , are normal values that store a value of any type that implements the given trait, where the precise type can only be known at runtime.

What is trait object in Rust?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.


1 Answers

Let's find out. I put this similar code in the Rust Playground and ran “Show Assembly”:

trait Foo {
    fn x(&self);
}
impl Foo for u8 {
    fn x(&self) {
        dbg!("xu8");
    }
}
impl Foo for u16 {
    fn x(&self) {
        dbg!("xu16");
    }
}

fn main() {
    let foo: &dyn Foo = &123_u8;
    foo.x();
    123_u8.x();
    123_u16.x();
}

In the (debug mode) assembly output, main is:

playground::main:
    subq    $24, %rsp
    leaq    .L__unnamed_12(%rip), %rax
    movq    %rax, 8(%rsp)
    leaq    .L__unnamed_2(%rip), %rax
    movq    %rax, 16(%rsp)
    leaq    .L__unnamed_12(%rip), %rdi
    callq   *.L__unnamed_2+24(%rip)
    leaq    .L__unnamed_12(%rip), %rdi
    callq   <u8 as playground::Foo>::x
    leaq    .L__unnamed_13(%rip), %rdi
    callq   <u16 as playground::Foo>::x
    addq    $24, %rsp
    retq

We don't need to be able to read every detail of x86 assembly to see that there are three function calls here, the last two of which are the static calls I added for comparison. So, .L__unnamed_2 probably has something to do with the vtable. What's that?

.L__unnamed_2:
    .quad   core::ptr::drop_in_place<u8>
    .asciz  "\001\000\000\000\000\000\000\000\001\000\000\000\000\000\000"
    .quad   <u8 as playground::Foo>::x

Looks like a vtable to me: it's referring to drop glue and to an implementation of x(). But there is nothing that does the same for u16 — the only reference to <u16 as playground::Foo>::x is the statically dispatched call in main.

Of course, this doesn't rule out that the compiler generated the vtable data, then threw it out before getting to the assembly listing. But if it did, then either that would be a compiler performance bug, or it would be so cheap as to not be worth worrying about.

(Also, as more anecdotal evidence: the Rust compiler is known to generate multiple vtables for the same type if they happen to be needed in separate codegen units.)

like image 197
Kevin Reid Avatar answered Oct 13 '22 07:10

Kevin Reid