I googled around and this has been asked before several times; there are
answers that use obsolete syntax and Rust concepts, so I thought it might
be a good idea to revisit the issue.
The basic use case is having some trait, and having some (base/mixin)
struct implementing it. Now, one wants to have a 2nd (derived/importing)
struct implementing the trait, based on the 1st struct implementation
(including its data members).
I'm not certain what the idiomatic Rust answers to this common use case is,
today.
One can obviously simply duplicate the data members and the trait
implementation, if the size of the code is small enough. This is very WET
though.
One can have the derived/importing class contain a member of the base/mixin
struct, then delegate each and every trait function to it (providing
overrides where needed). This is pretty tedious and results with a lot of
boilerplate code. Performance would probably suffer. It is also difficult
to override methods that are invoked by imported methods.
One can write the 1st struct in a way that is intended to be overridden,
e.g. by providing a trait with a "get base" accessor and using that all
over the place. This makes the base struct code uglier (and, I think, less
efficient), but results in less boilerplate code in the derived class. It
makes overrides much trickier though. In general seems pretty restricted.
One can implement a bunch of macros to automate injecting the common code
into derived structs (I think - I'm not clear whether macros are allowed to
add methods and members to a struct). Setting up the macros "isn't fun",
especially when one wants to take overrides into account.
If there another option? Which one is more "idiomatic" Rust code?
There are many options that would require compiler/language support...
these basically automate the above approaches.
Personally I am fond of a somewhat unusual approach I first saw in Sather
(I think), which was to automate the macro based approach. That is, define
"implementation inheritance" (actually, "mixins") as a source-level
operation.
The source code of the imported/reused members from the base (mixin) struct
would be (logically anyway) pasted inside the definition of the
derived/importing struct, and then compiled as usual.
This is very simple to define (it is basically similar to "use" - in fact,
this resonates with the idea of changing the division of labor between
structs and modules, but it is a separate discussion).
It allows for great flexibility: one can import only specific methods and
members, providing a different implementation for others; Using the ability
to import something under a different name, it is possible to wrap the
imported methods with additional functionality (foo { ...;
renamed_imported_foo(); ... }), etc.
So, this provides the functionality of "virtual functions" (an imported
function calling one that is redefined - that is, not imported but
implemented locally - will get the overriden behavior). At the same time,
unlike "virtual functions", it still allows for aggressive optimizations
(since when compiling a struct, all methods are fully known and can be
inlined etc.).
It sidesteps a lot of sticky issues with a vtable-sharing oriented approach
(like in C++). For example, re-importing would cause multiple definitions
of the same member/method.
It even allows for separate compilation (if the object file contains the
AST tucked in it somewhere), and for reuse of compiled methods (if they
don't invoke overriden methods - but that may be tricky to implement).
At any rate, I'm not claiming this is necessarily the best approach for
Rust; I'm just wondering, what is the proposed way to address this use
case? None of the manual approaches seems very appealing (unless there's a
better one I missed).
Thanks,
Oren Ben-Kiki
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev