I want to share results of my experiment and ask for help with progression. I hope it's not too chaotic.
I'm experimenting with trait objects and dynamic dispatch. My goal is to to create a trait object with methods set during runtime using TraitObject.
Here is what I've come up by now: playground. I've created my own TraitObject
with no actual data (size 0 and null data pointer) and a pointer to a fake vtable. It seems to be working, I put a regular function into the first fake vtable entry, casted TraitObject
to a &ToString
and successfully called it. It was easy, because this trait has only 1 method so it had to be the first one. The problem are bigger traits, especially deeply inheriting (even proc macro won't cope with this). There seems to be no way to find out which index is which method. I've come up with a few conclusions:
-
One could create an "even faker trait object" with a long (like 1024 entries?) vtable full of references to functions, where each of them sets a value of some global variable to a function's index. Each function takes no arguments and returns nothing. When the user wants to set some method of a fake trait, the even faker trait is called with pieces of uninitialised memory (it won't by consumed anyway) as arguments and the result is immediately forgotten (function didn't set it, so it must be junk). We can than proceed with changing the vtable of a fake trait object on index set in the static variable.
- Pros
- We've got a vtable index of a method!
- Cons
- In the beginning the fake trait object has vtable full of nulls, it will segfault when calling method not initialized manually
- Theoretically there could be a trait with more than 1024 methods (unlikely)
- Not sure, but some ABIs might want to retrieve the result of some type returned from the even faker trait object by pointer and immediate memcpy to the stack, that would obviously fail, because returned data is junk
- Pros
-
Create fake trait object based on a real one. This would require copying the whole
TraitObject
and cloning its vtable.- Pros
- We've got an immediately valid fake trait object, which then can be modified
- The base struct could have an easy to generate implementation of a trait with
undefined!()
everywhere
- Cons
- How many entries of a vtable should it copy? I've found no way to find length of a vtable or count of methods in trait. It could copy some arbitrary, big amount of data (1024 entries?), because the dispatch mechanism would only use as many, as needed. But the problem is possible segfault when reading redundant entries and I've found no easy and portable way to anticipate it or recover from a segfault. But it's somewhat possible.
- Pros
-
There is no way I've found to cast the trait object to a concrete method pointer. This could allow searching the vtable and find its index. But anyway there would be no guarantee that all methods will be distinct (some method could have implementation calling another method from the same trait, compiler could optimize out the delegation).
-
Modification of a real trait object's vtable could cause segfault (after all normally it's read only) and would destroy the real trait's behavior.
Is there anything else I could try out? Maybe I should propose intruduction of some intrinsic method or macro, that could identify method's vtable index? Could it be even possible to implement? Maybe prepare an RFC for dynamic trait object creation?