3 ms·
> Under the hood, a virtual table (vtable) is created for each class, and a pointer (vptr) to the vtable is added to each instance. Coming from C++ I assumed t
by gignico 7mo ago
> Under the hood, a virtual table (vtable) is created for each class, and a pointer (vptr) to the vtable is added to each instance.
Coming from C++ I assumed this was the only way but Rust has an interesting approach where the single objects do not pay any cost because virtual dispatch is handled by fat pointers. So you carry around the `vptr` in fat pointers (`&dyn MyTrait`) only when needed, not in every instance.
- dalvrosa 7mo agoGood point, thanks for sharing!
- cataphract 7mo agoThere have been type-erasure libraries in c++ for a longish time that allow choosing inline vtables and inline storage. It's definitely been a widely talked about technique for at least 10 years (I see talks about Dyno from 2017).
- gignico 7mo agoWell, Rust as well has been around for more than 10 years now. I don't imply Rust invented the approach. Surely academia knew about it decades before. I was rather commenting on how one's mental model of things can change by learning new languages.
- anon291 7mo agoThis is the standard type class approach. Haskell does the same thing.
- menaerus 7mo ago> only when needed Do you know how is this exactly deduced?
- dminik 7mo agoIt's not. The user has to decide. A specific type/reference to a type will always use static dispatch. fn foo(bar: &Baz) { bar.thing(); } A dyn trait reference will always use dynamic dispatch and carry around the vtable pointer. fn foo(bar: &dyn BazTrait) { bar.thing(); }
- menaerus 7mo agoAh, I see. Do I understand correctly that this means that for a given instance of polymorphic object I can switch between static polymorphism and dynamic dispatch, and use them both simultaneously? How is this useful in practical terms, like why would I want to do it?
- AnimalMuppet 7mo agoI think the question is, do you know at compile time what the concrete type is? In situations where you do, use static. (I'm not sure I'd call that "polymorphism". If you know the static type it's just a function on a type, and who cares that other types have functions with the same name?) But if you don't know the concrete type at compile time, then you must use dynamic dispatch. And you can use each approach with the same type at different points in the code - even for the same function. It just depends on you local knowledge of the concrete type.
- dalvrosa 7mo ago+1
- menaerus 7mo agoThat's polymorhpism 101, and not quite what I was asking. From my understanding what Rust has is something different to what C++ is offering. In C++ you either opt-in for static- or dynamic-dispatch. In Rust it seems that you can mix both for the same type object and convert between the two during runtime. It seems that this is true according to the example from dminik from the comment above but the actual purpose is still not quite evident to me. It seems that it tries to solve the problem of excessive template instantiations, called as I can see monomorphizations in Rust. In C++ this is normally and mostly done through the linker optimizations which may suggest that Rust doesn't have them yet implemented or that there are more useful cases for it.