4 ms·
I'm confused. In that example, what's wrong with `Vec<Box<dyn EventHandler>>`?
by couchand 6y ago
I'm confused. In that example, what's wrong with `Vec<Box<dyn EventHandler>>`?
- kelnos 6y agoI'm not an expert on this, but my guess would be that it's slow because it requires at too much indirection. Instead of the Vec holding concrete instances of EventHandler, it holds pointers to them (because of the Box). And then because of the dyn, the compiler can't generate static-dispatch code when you later inevitably want to call functions on the EventHandler, so those function calls have to be resolved at runtime.
- orf 6y agoIs it even possible to do it without indirection? The type parameter of a struct can change its shape hugely, so what you’re asking for is “vectors with variable sized entries” which is typically done with indirection.
- jcelerier 6y agoIn C++ we have things like Boost.PolyCollection to improve that specific case : https://www.boost.org/doc/libs/master/doc/html/poly_collection/an_efficient_polymorphic_data_st.html https://www.boost.org/doc/libs/master/doc/html/poly_collecti... With some metaclass / reflection magic maybe one could come up with an additional scheme that would save the per-object vtable pointer through creation of a "duplicate" structure - e.g. given struct Event { virtual ~Event(); virtual bool whateverAPI(); pair<double,double> globalPosition(); pair<double,double> localPosition(); private: double x{}, y{}; Widget& widget; }; struct MyEvent : Event { // or whatever, just an example bool whateverAPI() override; private: MouseButtons button{}; }; one would get a sub-array of things with a layout similar as struct synthesized_MyEvent { double x{}, y{}; Widget& widget; MouseButtons button{}; pair<double,double> globalPosition() { /* potentially UB magic */ } pair<double,double> localPosition() { /* potentially UB magic */ } bool whateverAPI() { /* potentially UB magic */ } }; ("potentially UB" being something like copying all the non-vtable things into a stack-allocated concrete MyEvent and calling the virtual function on that which could be worth it for small objects in terms of cache usage if the vtable is 8 bytes and the custom data something like 16 bytes and there's 350000 objects)
- gpderetta 6y agoSometimes you do not need the vector to be runtime sized, then you can use tuples. In C++ variadic templates make it easy to work with compile time sized collections of objects. I'm sure you could do something similar with rust macros.
- clappski 6y agoIn C++ you could do this with std::tuple and variadic templates, if you know all the event handlers at compile time. There would be zero indirection, although depending on how you implement the indexing into the std::tuple (i.e. by treating it as a std::variant and using std::visit) you could still inadvertently generate a vtable.
- gpderetta 6y agothis requires the set of all possible types to be known statically at the point the tuple is defined. True "virtual concepts" are an open set.
- masklinn 6y agoHaving dynamic dispatch of boxed structures seems like a logical consequence of wanting dispatch on arbitrary user-provided non-coherent (not the right word but I can’t remember the proper one) types?
- lmm 6y agoThat's what a trait object would have to be anyway though? You're going to have to have a vector of virtual function tables at runtime, one way or another.
- saghm 6y agoNothing is wrong with `Vec<Box<dyn EventHandler>>` itself; the issues are around the ergonomics of using trait objects in general (e.g. with regards to requiring that types that implement EventHandler also implement Clone, as mentioned in the parent comment).