"Inheritance" through composition


#1

What do you think about Go-like inheritance-like system that will provide it via composition. Example:

struct Parent {
    a: u32
}

struct Child {
    super: Parent,
    b: u32
}

struct GrandChild {
    Child, // the same as `super: Child`, questionable syntax
    c: u32
}

When there will be present field named super then there will be “autoimplemented” Deref and DerefMut traits. So in above example call grand_child.a would looks like grand_child.deref().deref().a. The only incompatibility would be need for additional pseudostruct like Self named Super that will provide way to call static methods from super (non-static methods can be called via self.super.foo()).


#2

What you describe here is the “deref hack” for pseudo-inheritance. Note that the compiler needs to insert the appropriate deref()s, so as far as performance goes, this is rather suboptimal (especially as your hierarchies grow in depth), as each deref involves a type check (granted, the effect will usually be negligible, as branch prediction will be 100% accurate, but still in hot code those few cycles add up). Also this hack only can implement single inheritance.

The currently debated proposals AFAIK all rely on vtables to make the dispatch which is faster, but requires some preconditions to be met. Those preconditions vary between proposals and some of them are incompatible.


#3

The type checks would be done at compilation, and the derefs should be inlined, so it should be free at runtime, or did I forget something that prevents that?


#4

That’s correct; in particular, there are no runtime pointers/indirections involved in such a representation.

The main problem with this suggestion is that it doesn’t cover virtual dispatch: if you deref a Child up to a Parent and then call a method, you’ll be getting the implementation on Parent; there’s no way to override it. I recently wrote a blog post covering the main way the lang team has been thinking about providing virtual dispatch, namely, via trait specialization.


#5

Virtual dispatch if often a bad thing …due to the ease of having the wrong implementation after refactoring’s , cross lib performance and the fact that in most cases more and more behaviour gets thrown into the base and then you get the fragile base class problem. Good as an option for those who know their stuff but personally I would like developers to be steered away from it since old bad habits die hard.

What’s wrong with trait where clauses ? Only thing I can see is Performance.

A far more maintainable method is to inject the behaviour ie store a function pointer / closure in the struct and inject when you build it. If performance is an issue than this seems a reasonable non premature optimization if the cost of a trait is too high ?