Adding true OO capabilities to Rust

  1. Encapsulation
  2. Multiple inheritance
  3. Polymorphism
  4. Metaclasses
  5. A clean design (like Java or C#, and not like Python, JavaScript, or other languages that tack on OO capabilities as a clear after-thought)

I am not going to rationalize these features because they already have been ad nauseam. I've found these feature to be incredibly useful when managing large, complex applications.

Rust already has encapsulation (struct with private fields and methods), polymorphism (generic functions and trait objects), and a clean design. Rust has also explicitly chosen to favor composition over inheritance. If you want to be able to give useful suggestions for the design of Rust, you should first familiarize yourself with how things are typically done in Rust.

36 Likes

I took a brief look when I first came across Rust. The OO capabilities of Rust appeared minimal and clunky to me. It reminded me of how JavaScript used to do it and how Python does it. It appears clunky, unnatural, and piecemeal. (Sorry. Just being honest.)

If I want to do X, I have to do A, B, and C and it is, effectively, the same. I want something that is clean and straightforward (like Java, for example) rather than something that is effectively the same.

Given that JS does prototypical inheritance and Python has MRU-mediated behavioral inheritance, I don't see how these at all resemble Rust's mechanisms for doing it.

Can you please be more specific. What is "X" here? Or are we just playing buzzword bingo with what patterns Rust has built-in?

"Clean" and "straightforward" are very much a taste thing. For example, having known Haskell before, Rust's trait system is way more "clean" and "straightforward" than coming from C++. Just saying you want some nebulous result without being specific is no way to get what you want because whoever takes this cause and implements it may have a very different view of what it means than you do.

32 Likes

When I look at https://raw.githubusercontent.com/blakemcbride/Dynace/master/manual/WDS.pdf I see a UI framework, which would just be a library, not a language change.

Is there a writeup somewhere of specifically what things you think Rust should adopt from it?

If you could write this in C with zero built-in features for it, why couldn't you write it in Rust too?

8 Likes

All the languages you listed implement OO in different ways, with very different semantics. You should be more specific what you want. Having OO in some other language also doesn't mean that it will fit in Rust with its design constraints (zero-cost abstractions, correctness by construction, explicitness), so saying "these things were discussed many times" doesn't help anyone. You need to make case why specific features are a good addition to Rust as it stands.

I'll also note that there is plenty of stuff written about why OO is bad, a dead end and a huge mistake. Most recent languages, including Rust, have deliberately avoided OO idioms. That makes it doubly important to state your case if you think Rust is really missing some important feature.

22 Likes

Yes, WDS.pdf is just a GUI library on top of the language extension. The language is described in https://github.com/blakemcbride/Dynace/blob/master/manual/Dynace.pdf

I believe this extension could likely be re-written for Rust.

Getting the kernel of Dynace to work took me less than a day from scratch. I spent years on the rest which was all very straight forward once I had the kernal built. (https://github.com/blakemcbride/Dynace/blob/master/kernel/kernel.c)

Given that JS does prototypical inheritance and Python has MRU-mediated behavioral inheritance, I don't see how these at all resemble Rust's mechanisms for doing it.

I am not saying it is the same as Rust's. I am saying it is piecemeal and hacky - not clean and clear.

I'll also note that there is plenty of stuff written about why OO is bad, a dead and and a huge mistake. Most recent languages, including Rust, have deliberately avoided OO idioms.

After 40 years of programming, and coding nearly 12/7, it is my professional opinion that full and clean OO facilities are a huge, huge benefit to the developer community. In terms of bad press:

  1. In my professional opinion, they are wrong and missing the point
  2. I have discovered that you can think literally anything and someone out there disagrees with you (hello Flat Earthers)

That makes it doubly important to state your case if you think Rust is really missing some important feature.

Sorry. Rationalizing this and getting past the political stuff would require more time and effort than I have. My ideas are represented in Dynace. Use what you like and throw out the rest.

And I think Rust's is anything but piecemeal and hacky. I like it quite a bit more than what Python, C++, or Java has provided. Now what?

In particular, the ability to implement an interface for a type without having to name it on the type itself is a huge benefit. It means that I can keep the declaration for the type to just what matters: its data members. The API for the type beyond that can be implemented in any order elsewhere in the module (or, in the larger scope, crate) instead of needing to be right beside the declaration.

Inheritance is a bad default to provide over composition. Types are covariant or contravariant depending on the mutability of the API. For example, the canonical:

struct Ellipse {
public:
  void set_major(int x);
  void set_minor(int x);
};

struct Circle : public Ellipse {
  // So what does `circle->set_major` do here?
};

means that an Ellipse& is actually dangerous because if it is actually a circle, set_major and set_minor actually twiddle the same thing. This feels quite dirty to me when trying to apply the Liskov Substitution Principle and expecting get_major to not care about set_minor calls.

Rust's trait system instead pushes you to think about interfaces that actually matter (here, probably Shape with area and perimeter methods. But mutability is insane to have in such a mechanism because shapes can have all kinds of free parameters that it would never be possible to enumerate everything.

Digging into some library I've never heard of to look for…something that isn't on anyone here's schedule either.

If you just want to showcase something, then be clear that that is what you are doing. Don't put something on the table and say "something should be done" and refuse to explain when folks ask you what to do with your proposal.

AFAICT, "full and clean" has never been done before, so claiming its benefits would seem to be quite premature IMO.

13 Likes

I read the first 60 pages of the 400-page Dynace manual that you linked to, and I was left with absolutely no idea what you mean by "a clean and clear design of [encapsulation, multiple inheritance, polymorphism, and metaclasses]." I saw a language with roughly the same functionality provided by the original implementation of C++ (namely Cfront), plus garbage collection, and also roughly the same limitations, such as its not being possible to subclass int, which is the kind of thing I think characterizes a language that has "tack[ed] on OO capabilities as a clear after-thought"!

You have got to explain yourself better if you want anyone to adopt your ideas, dude.

38 Likes

Conversational side note: this thread will go nowhere if we bikeshed OOP vs non-OOP. I don't think it's fair to say one is definitively superior or flawed, and if we go down that road nothing positive will come from this.

Blake, I highly recommend you build some concrete examples of what you want and how they would fit into Rust. Not high-level abstract concepts. But concrete specifics.

It's possible to have a fruitful discussion regarding OOP. For example, while I like Rust's opinions here, I also need to interact with C++, Objective-C, etc, and this can get awkward at times when trying to mate two different languages. I could provide specific examples of the awkwardness, but I won't because I actually don't really care to participate in this discussion. I mention this only to give an idea that if you want to have a productive conversation here, things are going to have to be discussed concretely and specifically.

31 Likes

The lack of C++ like inheritance is usually something that bother newcomers that are used to OO languages, but rarely a real problem in practice for more experienced users. I agree with others that you should provide concrete examples of the things that feel wrong to you, because what I feel from your messages is just that the Rust language is not like the ones you are used to.

If I remember correctly, there used to be plan to introduce C++ like inheritance in the pre 1.0 era because the Servo project needed it. But as the language matured, they realized it was not really necessary and the trait system was enough to cover the Servo use cases.

13 Likes

Defining a class - see https://github.com/blakemcbride/Dynace/blob/master/examples/exam13/class1.d

Using that class - see https://github.com/blakemcbride/Dynace/blob/master/examples/exam13/main.c

Comparison with C++ - see section 1.3.1 of https://github.com/blakemcbride/Dynace/blob/master/manual/Dynace.pdf

I completely agree. However, I am so overloaded that I do not have the time. I spent many years on Dynace. The project proved to be a huge benefit and success. Since it is open-source, I am hopeful that some of the ideas can be beneficial to the Rust effort.

I looked over many of the new languages that appeared. I typically don't like them. However, I do like what I saw in Rust. Over the years, I have acquired a great appreciation for the OO concepts I spoke of. Seeing them cleanly implemented in Rust would be a big benefit to the community, IMO.

Thanks!

Blake

I would like to add that the Rust designers have put a lot of thought into the basic principles of the language, including the decision to not be fully OO.

I'm not saying this is impossible to change; but it is somewhat unlikely that you'll come with something new, and especially unlikely if you don't consider the integration with the rest of the language.

Unfortunately, those discussions are hard to find (pre-1.0), but they were there.

4 Likes

That is completely understandable, and there's nothing wrong with that. But, to be clear, Rust is primarily built by the people who do put in the time, including the time to communicate ideas and points of view and understand each other's points of view and try to find common ground.

In general, we do regularly take a look at many, many other designs and concepts and capabilities out there, and think "is there something in that that we want to add". A fair bit of energy has gone into Rust's object system, and it has had no lack of consideration of OO features. Most importantly, though, we don't look at another design approach and say "let's systematically add everything from that design whether we want it or not"; we're not looking to tick a box (and in any case to many people we've already ticked that box), we add things because they would benefit Rust's users, and we evaluate them on that basis.

So, if there are specific capabilities we should add, that fit in well with the rest of the language, that provide more benefit than cost (including the baseline cost of incrementally increasing the language surface area), we may well add them. But we can't meaningfully act on a nonspecific direction like "more OO" without a lot more breakdown of what precisely we need.

As one example of a capability we do want to add, which will likely benefit those seeking inheritance: we've talked quite a bit about adding delegation, so that you can say "I implement this trait by forwarding all the methods to this field, and then override this specific method".

51 Likes

In languages there are many valid approaches, each with different trade-offs. Rust has equivalent of a "class" via struct Foo {} + impl Foo {}.

This clearly isn't what you're used to or what you like, but it is elegant in its own way: separates definition of data from code, and avoids having duplication of struct and class in the language that are almost the same thing (in Rust they are the same thing).

The impl syntax also combines well with generic trait bounds. This is another intentional design elegant in its own way. Instead of introducing an additional template class syntax, there's one impl syntax that adds method to a type, whether they're inherent methods or come from traits, and the methods can be available conditionally depending on generic parameters used with the type (e.g. a vector can be serializable only if its element type is serializable).

Rust has a form of multiple interface inheritance via the trait system. A single type can implement multiple traits (traits behave like Java interfaces in this case). Here's another advantage over a classic class approach: other types, even primitive types like integers, can also implement traits without having to be objects or inheriting from something specific.

21 Likes

Experience might correlate strongly with wisdom & knowledge but it does not entail it.

There's so much wrong here:

  1. C# exists because Microsoft wanted to extend Java in incompatible ways which was illegal according with Sun's terms and conditions. So no wonder it has the same OO implementation. Anyone who actually has any experience with either knows that best practices are to avoid inheritance as much as possible. "Composition over inheritance". This isn't a new concept either - the famous gang of four recommended the same in the early 90s and their design patterns in large part are in fact the results of applying said principle.

  2. Popularity of Java style OO has many reasons. Putting it all on some superiority of its OO style is oversimplified and incorrect. Java popularity has a lot to due with its promise of portability and simplicity to name just two other important factors that have nothing to do with its specific flavour of OO.

  3. Someone your age should have been more knowledgeable about the history of OO. The original idea comes from Simula and Smalltalk to name a few prominent precursors. Smalltalk for example has a very different style compared to what you're familiar with in Java. Smalltalk was very popular and it imploded unto itself due to reasons unrelated to its OO flavour.

To summarise, one specific implementation that you've been using for 40 years, isn't the only possible design nor was it the first, nor most popular. It certainly isn't the one and only design exposing "true capabilities" as stated.

More over, the attitude of "I'm not going to explain myself" is condescending and inappropriate. You came to tell us that Rust is crude and inefficient in your opinion yet you haven't bothered to understand what and why idiomatic Rust is. Haven't you been taught "When in Rome, do as the Romans do"?

15 Likes