How to Implement inheritence in rust?

Let say we have a C++ program:

class shape
{
int color;
public:
int getColor()
{
return color;
}

};

class rectangle: public shape
{
double length;
double width;

public:
double getLength()
{
return length;
}
double getWidth()
{
return width;
}

};

class circle: public shape
{
double radius;
public:
double getRadius()
{
return radius;
}
};

How can I convert this code in rust?
I have used traits for this, but the problem is I have to repeat the implementation of get_color() method for all structs.
Is there any other method to convert this code so that it works same as C++ code without repeating any method.

1 Like

If you know all the possible implementations, you should use an enum together with composition. There is no need for accessor methods in this simple case.

enum Shape {
   Circle { radius: f64 },
   Rectangle { width: f64, height: f64 },
}

struct ColoredShape {
   shape: Shape,
   color: u32,
}

However, I assume this is only a toy example; in this case you might have to provide more insight into your actual use-case to get more help.

2 Likes

It will also be easier to help if you format your code as per this pinned post.

1 Like

Half of the How not to learn Rust deals with the desire to write C++ in Rust, JavaScript in Rust, Python in Rust or even Fortran in Rust.

It's bad idea almost every time.

If you have to deal with the existing codebase then there are ways to deal with it, if you want to bend Rust to your will and make it OOP language… it's not impossible but entirely pointless: you would spend so much time fighting Rust that it would really be easier to just write you program in C++.

5 Likes

Rust doesn't support type inheritance, but that's deliberate.

You can get the extensibility inheritance gives you in two ways:

  • "Closed", where you have every possible type in the same place and the behavior is spread out (generally with match statements), using enums (as mentioned above), or
  • "Open", where you have the interface to interact with in one place and the possible types spread out together with their implementation of the interface, using traits.

Or you can combine the two.

The specifics are a bit much to get into in a comment, give the book a look through first for an outline of what you can do with them, but feel free to ask more specific questions!

4 Likes

Just want to note that it's not deliberate in a sense that someone decreed that “OOP shalt never be added to Rust”, but in a sense that OOP itself have inborn flaws which make creation of robust programs problematic except via “you have to hold it right”… and Rust doesn't like “you have to hold it right” language constructs.

I have no idea really. Better I let other's more experience in Rust advise.

But, having faced this kind of problem I started out by questioning the whole architecture of what I'm dealing with in the first place. Often one finds it makes little sense. For example I was given some example code in C# that decoded a weird proprietary format packet stream. Turned out every class ended up depending on every other class in some unfathomable cyclic mess of dependency.

To the case in point. The example code here starts off with a class called shape. But the implementation of shape has nothing at all to do with shapes in it. Nothing to do with size, geometry, edges, vertices, angles or any shape like information. It only contains color information. Shapes are not defined by their colours. This makes no sense at all!

What we see here is that the class hierarchy has been cobbled together as a way to stuff a colour property into two totally different things, circle and rectangle. It does not reflect anything meaningful to the problem. Using class hierarchies simply to reuse code is not a good design strategy.

Where I end up is that OK, I have a piece of code in C++ or whatever language. Rather than try to transcribe that, almost that line by line, into Rust it is better to take it as just a suggestion. Treat it as a specification what one wants to do, not detailed design of how to do it.

3 Likes

I think originally it was more so that inheritance doesn't compose well with traits. Traits can be implemented for a type T far from where T is declared. In Rust methods are not—generally—attached to objects via a vtable, they are statically known, where in languages which support inheritance you almost have to build such a vtable for each type (or each instance of a type).

1 Like

Yes, but that explains how you can develop programs in Rust without OOP, it doesn't explain why “classic” Simula 67-style OOP was never added to Rust.

And it wasn't added because OOP is fundamentally flawed. It's three pillars (inheritance, polymorphism, and encapsulation) are incompatible.

Usually people start with inheritance, polymorphism, then try to add encapsulation… and it doesn't work.

Because the official OOP-term for a property that ensures that your program is doing something useful and not just shuffling random bits around is called LSP and it looks innocent enough:

𝑆⊑𝑇→(∀𝑥:𝑇)ϕ(𝑥)→(∀𝑦:𝑆)ϕ(𝑦)

In layman terms in says: for every property ϕ you have to be able to replace superclass with subclass… but what is ϕ? What kind of property is that?

The answer is hilarious and sad: ϕ is any property which your program may need!

It can not be “any property imaginable” (because then you wouldn't be able to use classes which differ in even minutiae details, which would defeat the purpose of OOP). It can not be “any property from the list of properties defined in advance” (because there are no limits on how you may [ab]use these types). It have to be any property which your program may need… and how do you plan to ensure that?

Basically: you can not, really, ever say whether inheriting from rectangle from shape would work or not — except if you study the rest of the programs that rely on these two types (millions, sometimes billions of lines) and collect all the important properties these millions (or billions) of lines may need.

What kind of encapsulation is that? How can you say “it's Ok to inherit from shape and add length and width fields… except when you program does certain things which make it illegal”?

Well… in the “you have to hold it right” language like C++ or “if there are no memory corruption, then we don't care whether your program works or not” like Java it's acceptable approach.

But in Rust… in a language designed around the idea that compiler have to know about your program to stop you from doing mistakes… that if A is B in one place then A is B in any other place, always and forever approach… it just doesn't fit!

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.