From @eddyb and @Kimundi’s proposal:
“People coming from other mainstream languages are usually familiar with type inheritance, which will make them overuse this feature in places where a trait-based design would be the better choice.”
I couldn’t agree more. Your average user will get more out of rust of inheritance is not easily available. Orthogonality with existing features should be the first priority, and ergonomics might even be considered a bad thing.
Also, there seems to be equal, if not more, interest in the performance characteristics of C++'s implementation of inheritance than the expressitivity gained from the language feature itself. This makes me thing the true solution is in fact some fancy system for existential types. Regardless, a proposals that doesn’t offer enough control over the runtime representation, may the miss the mark or even do more harm than good.
The more low-level proposals (@MicahChalmer’s, @eddyb and @Kimundi’s) gave us maximal control over the runtime representation, maximal orthogonality, and minimal ergonomics, and minimal change to the language, so I think they are a clear win. (They seem pretty reconcilable too.)
RFC 91 together with a few standard macros seems, to me, to be an elegant solution. I particularly like that the Coercible and HasPrefix traits provide a means to generalise type relations.
A pattern I’d like to praise is the use of structs with single fields (or newtypes), for safety and for clarity. From the RFC:
pub struct Age {
pub age: uint
}
Being able to convert a HashMap<K, Age> to a HashMap<K, uint> impressed me.
I have another proposal: https://github.com/rust-lang/rfcs/pull/245. This takes the nice bits from #142 (enhancement and ‘unification’ of structs and enums), but uses traits for virtual dispatch rather than a custom system around concrete impls.
This is the only proposal that invoked the following initial reaction from me: “Yes.”
This proposal basically extends various aspects of traits to handle inheritance, but at the same time the components in this RFC doesn’t feel like bolted on specially for inheritance support, and each is useful in its own right even without a need for (full) inheritance. Their inclusion in the language can be very natural in my opinion.
And most importantly, I don’t think people will have a tendency to overuse inheritance with this RFC, or rather, there is still only one true way of method dispatch - traits, so use of inheritance doesn’t confilict with the current Rust style.
As a newbie to Rust, that syntax looks pretty scary to me. The whole struct HTMLImageElement { vtable: Vtable<Element, HTMLImageElement> ...} feels like needless boilerplate. C++ syntax struct HTMLImageElement: public Element looks better by comparison.
The Vtable and friends are basically a “low-level” way to do a performance optimization, as long as this is made possible, we can have macros/attributes that are nice looking and easy to use for expressing this. Also, this way, normal code that doesn’t need C++ style internal vtables are not affected and works like today.
My first reaction, rather than “yes”, was “uhhhhhhh let me read that again?”
It would definitely be better if Vtable were relegated to library code like the implementation of Fat and never needed in actual inheritance hierarchies.
The struct HTMLImageElement : Element syntax might even be a good way to implement associated fields, although it would break the intersection of associated fields and FFI struct layout.
This may sound weird, but I like the fact that inheritance is difficult/“ugly” to use, because it makes people less likely to choose to use it rather than generics and traits.
It does sound weird.
Naturally, a language design that makes code ugly or difficult to write – and therefore to read – is the worst possible language design. It’s a failed design. Good design allows you to express your ideas using a small subset of orthogonal constructs without ambiguity or redundancy.
We already have a good design, they are traits.
Making inheritance difficult to use in Rust shows that we don’t want you to use it except if you are certain that there are no other solution.
I worried about this too. But this problem doesn’t quite exist when inheritance is based on traits. C++ has this problem because its template/concept system is completely bolted onto the object system. But Rust can unify all of them into the enhanced traits, of which RFC PR 250 is the most seamless IMHO. RFC PR 223 takes more of a “building blocks” approach and do not directly enhance (normal) traits (the author is planning to adopt parts of PR 250, it seems).
I believe the good bits of several RFCs can be combined to form a satisfying solution which is flexible and hard to misuse.
I have good faith in the Rust community and this discussion. A good solution will be found that is pleasant, performant, hopefully derives advantage from the trait system.
It’s based on an idea from this issue that never really made it into an RFC on its own, which is one of the simplest versions that’s still nice and orthogonal.
*make Rust data structures more powerful, more orthogonal, and more
unified. This includes data inheritance which, in keeping with the
data/behaviour separation in Rust is totally separate from trait
inheritance/implementation.
*Add behaviour inheritance which follows data inheritance when
implementing a specific trait. I hope this feels natural as a way to
share behaviour between related data types.
*Offer optimisations for speed and space using the closed and unsized annotations.
I hope that this addressed the two big (concrete) criticisms of
earlier proposals about the near-unification of enums and structs, and
the inheritance in trait-less impls.
You’re not supposed to use it unless you really know what you’re doing, and then it’s an opt-in layout optimization that sacrifices flexibility for some domain-specific gains.
It’s not a kind of “inheritance”, and I would compare its “user friendliness” to #[repr(C, packed)].
It’s not intentionally ugly or obtuse, but rather verbose: clearly stating your intent to change the representation of trait objects of a given trait to “thin pointers”, forcing single inheritance chains in the trait hierarchy (which does not limit it to single inheritance relations everywhere, btw).
The mechanism could be extended in the future to allow C++, COM or Objective-C interop.
The Vtable primitives seem complicated because they use type safety to avoid requiring unsafe code.