Adding true OO capabilities to Rust

Greetings,

It is my opinion that true OO language facilities as a development tool is a proven and extremely valuable feature just as structured programming was many years ago. I won't go on about the reasons because those reasons are documented all over the place. I believe it also, in part, speaks to the popularity of languages such as Java and C#.

Being frustrated with many aspects of C++'s approach and having over 20 years of experience with C, I decided to add my own OO extension to C. The extension I wrote was a complete success and has been used commercially for about 15 years now. I open-sourced it at https://github.com/blakemcbride/Dynace

My reason for sharing this is not to promote my extension. Rather, I am hoping the Rust language developers take a look and consider adding something like this to Rust.

Thank you.

Blake McBride

"Object oriented capabilities" is a very open-ended category. It would help if you gave specific examples of things that you think Rust is lacking, and what OO capabilities you think would help with them.

25 Likes
  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