This is probably as good as we can get in the current system (I do
something similar in my code today).

I also think you probably need both "super" and "mut_super", so it would be
two methods to implement instead of one (still pretty good). I wonder
whether it is possible to write a macro that automates writing these
boilerplate methods?

The key weakness is that (I think) the compiler can't inline the accesses
to "super" so that you end up with a chain of virtual function calls every
time it is accessed, so performance would be pretty bad.


On Wed, Nov 13, 2013 at 10:27 AM, Eric Reed <[email protected]>wrote:

> Here's how I would do it using just existing Rust (assuming this hasn't
> all changed under me in the past couple months).
> NB: I haven't actually tried compiling this, but I'm pretty sure it (or
> something like it) would work.
>
> Nice properties over this solution:
> - Doesn't require language extensions (although syntax sugar wouldn't be
> unwelcome)
> - Doesn't require trait objects (i.e. static dispatch is possible)
> - Only need to implement one method for each derived type (super) in
> addition to overridden methods
> - Supports multiple inheritance in two ways (and avoids the diamond
> problem I think -- not a C++ expert so I may have misunderstood that)
>       + no default parent and programmer must select which parent to use
> before calling
>       + implementer-chosen default parent and programmer can chose to use
> a different parent if desired
>
> Neutral?:
> - Doesn't enforce or care about the prefix property. Not sure if that
> still matters so much w/o dynamic dispatch.
>
> Downsides:
> - Performance of delegation depends on LLVM's ability to inline (I think).
> - Does require repeating all the methods once (for delegating default
> implementations)
>
> // The base type
> struct Base {
>     data : int;
> }
>
> // Characterize it's extensible behavior in a trait
> trait Trait {
>     fn method(&self);
> }
>
> // Implement the base behavior
> impl Trait for Base {
>     fn method(&self) { ... }
> }
>
> // Extension of trait that supports upcasting to existing implementations
> trait DerivingTrait<P : Trait> : Trait {
>     // one extra method for accessing a parent's implementation. ideally
> this would be inlined by the compiler
>     fn super(&self) -> P;
>     // default implementations for all the methods in Trait let us avoid
> writing delegation everywhere manually
>     fn method(&self) {
>          self.super().method() // just delegate to parent
>     }
> }
>
> // Single inheritance
> struct Single {
>     parent: Base,
>     moreData: int,
> }
>
> impl DerivingTrait<Base> for Single {
>     fn super(&self) -> Base { self.parent }
> }
>
> // Overriding behavior
> struct Override {
>     parent: Base,
>     otherData: u8,
> }
>
> impl DerivingTrait<Base> for Override {
>     fn super(&self) -> Base { self.parent }
>     fn method(&self) { ... }
> }
>
> // Multiple inheritance
> struct Multiple {
>     single: Single,
>     override: Override,
>     evenMoreData: ~str,
> }
>
> // must specify which parent's implementation we want (could hide wrapping
> inside of as_* methods impl'd on Multiple if you like)
> // if we want one of them as the default, then we can impl DerivingTrait
> on Multiple directly
> struct MultipleAsSingle(Multiple);
> struct MultipleAsOverride(Multiple);
>
> impl DerivingTrait<Single> for MultipleAsSingle {
>     fn super(&self) -> Single { self.single }
> }
>
> impl DerivingTrait<Override> for MultipleAsOverride {
>     fn super(&self) -> Override { self.override }
> }
>
> fn main() {
>     let base = Base { ... };
>     let single = Single { ... };
>     let override = Override { ... };
>     let multiple = Multiple { ... };
>
>     base.method();
>     base.super(); // compile time error
>
>     single.method(); // =inline delegation=> single.super().method()
> =inline upcast=> single.base.method()
>     override.method(); // done! no delegating
>     MultipleAsSingle(multiple).method(); // =delegate=>
> MAS(multiple).super().method() =upcast=> multiple.single.method()
> =delegate=> multiple.single.super().method() =upcast=>
> multiple.single.base.method()
>     MutlipleAsOverride(multiple).method(); // =delegate=>
> MAO(multiple).super().method() =upcast=> multiple.override.method()
> }
>
> Thoughts?
>
> Eric
>
>
> On Tue, Nov 12, 2013 at 10:30 PM, Kevin Ballard <[email protected]> wrote:
>
>> On Nov 12, 2013, at 10:26 PM, Kevin Ballard <[email protected]> wrote:
>>
>> > And even that restriction could be lifted if ~Trait objects could be
>> represented using an array of pointers (one to each inherited struct), e.g.
>> ([*A,*B,*C],*vtable) instead of just (*A,*vtable), though I suspect this is
>> not worth doing.
>>
>> Upon further reflection, this  would need to be done anyway because of
>> the ability to combine traits. If I have
>>
>> trait TraitA : A {}
>> trait TraitB : B {}
>>
>> and I want to use ~TraitA+TraitB then I would need a "fat" trait.
>> Although in this case the number of value pointers is equal to the number
>> of combined traits, so it's a bit more sensible to allow for "fat" trait
>> pointers here.
>>
>> -Kevin
>> _______________________________________________
>> Rust-dev mailing list
>> [email protected]
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>>
>
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to