15

!rant
So, Rust again.
When I learned that Rust doesn’t support inheritance, only traits (interfaces), I was shocked at first.

Then I tried to remember when the last time was that I have used inheritance in the code that I write (not the code that I use).

And I could remember an instance some months ago. But I also remember that I was very unsatisfied with that design and refactored it to use composition instead. And it was much better.

One of Rust’s properties is that many good practices in other programming languages are enforced rules in Rust.
And in case of inheritance, it seems like Rust decided that composition over inheritance is such a good practice that it should be a rule.

I’m not 100% convinced that there never will be cases where inheritance is better. But I still like this radical idea of forcing the devs to do it "the right way" in the majority of (if not all) cases.

I think many devs will disagree.
What do you think?

Comments
  • 6
    I think Rust is based and I love how it uses its struct enums to replace Exceptions and null values as well.
  • 1
    @localpost yes! That’s also how it is done in Swift and is one of my favorites.
  • 4
    The main usecases for inheritance are function signature enforcing, code reuse and polymorphism

    In Rust:
    Function signature enforcing - traits
    Polymorphism - traits via dynamic / static dispatch
    Code reuse - composition & generics

    So we got all we need. Composition is better than inheritance IMO because people never learned (or never wanted to learn) how to structure object cleanly so that they ONLY have things they absolutely require and nothing just because "its convenient".

    Composition forces you to do that properly and puts you in that mindset so you write better code.
  • 3
    You have a class and a set of functions that operate on that class. But now you want to add to the that class, but still use the same functions to operate on the new class. How does Rust solve that? Is there some kind of duck typing?
  • 0
    @arekxv that’s a good point
  • 0
    @Demolishun I’d say you should question why you want the same functions to operate on the new class.
  • 1
    @Lensflare I have a node system for a server I wrote. Everything works from the same base class. Every node is derived from this class. So every object has base node capabilities. If I want a new node type I inherit and that node now can work with the node system. Each new class is a specialization.

    How does Rust solve this?
  • 1
    @Demolishun Sounds like s trait thing, but I dont know Rust yet.
  • 1
    @Demolishun changing (extending) behavior can be achieved by swapping components that themselves have traits (implement interfaces).
    Instead of overriding methods.
  • 1
    I like to be able to do multiple inheritance of some sort - even though compositions almost always is the better way to do it.
  • 3
    Again, grumpy momma pointing sth out...

    What Rust does is - simply put - a far more fine grained type of object relationships.

    Fine grained as many forms of OOP simply suck bonkers because they allow things that will hurt you a lot... Most of these forms can hardly be put in a "single word definition".

    Which is why rusts documentation on e.g. traits is rather long and nitpicky - small details matter, especially in programming.

    E.g. multiple inheritance can be made sane and safe - if you ever struggled with the diamond problem, you realize that safe and sane requires to be careful how you design the inheritance.

    Same goes for e.g. overloading - it _can_ make sense to use multiple inheritance and overload (e.g. command pattern) - but the more "layers" (depth of inheritance)… the less you'll understand wtf is going on.

    Inheritance is - even if you try to put boundaries in (e.g. things like final keyword, scoping, ...) - a construct that is fragile.

    You're basically saying: I want everything to be the same, but different, under certain conditions, while still pretending to be the same.

    Head hurts reading the sentence? Good.
  • 2
    @Demolishun You would solve it through trait. You can think of a trait as an interface in OOP.
    You implement that trait for all of the objects you need and then at the place you usually call the interface function in OOP you call one of the function implementations for dispatch and pass the object. You can either implement a generic (template) type function (static dispatch done by a compiler) or implement a function using a dyn keyword of the trait you defined (dynamic dispatch done on runtime).
  • 0
    @arekxv very interesting. Thanks
  • 1
    Given how much inheritance get abused I like that choice
Add Comment