I'm not sure I follow.
My implementation doesn't use any trait pointers, so the only time there
were would be a trait pointer is if you casted to ~Trait yourself.
In that case, only the original method call would be dynamic dispatch; all
the internal calls (delegating and upcasting) are still static dispatch.
So my version doesn't pay for any dynamic dispatch over what the programmer
is already paying for.


On Wed, Nov 13, 2013 at 3:46 AM, Oren Ben-Kiki <[email protected]> wrote:

> The call isn't statically dispatched when I invoke a method via a trait
> pointer. So it seems when I invoke any trait function, I pay double the
> cost of a virtual function call instead of one... I suppose it isn't _too_
> bad, but it still hurts.
>
>
>
> On Wed, Nov 13, 2013 at 12:21 PM, Eric Reed <[email protected]>wrote:
>
>> I'm not clear on why LLVM wouldn't be able to inline super() calls. It's
>> static dispatch, so it knows exactly what function is being called.
>>
>>
>> On Wed, Nov 13, 2013 at 1:25 AM, Oren Ben-Kiki <[email protected]> wrote:
>>
>>> 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