1

C++ allows class subtyping, which is really convenient because you can use functions implemented for the base class with the derived class. Rust doesn't appear to have anything like that. The feature seems to have been available at some point, but has been removed since. Is this not possible in Rust? If so, are there any plans to have this feature?

What I want to do is define a struct that inherits from another struct, which in C++ would look like:

struct Base {
    int x;
    int y;
    void foo(int x, int y) { this->x = x; this->y = y; }
}

struct Derived: public Base {
    ...
}

void main() {
    Derived d;
    d.foo();
}

The way I see it, in Rust you have to write something like this in order to make the same functionality available for all 'derived' structs:

struct Base<T> {
     x: i32,
     y: i32,
     derived: T
}
impl<T> Base<T> {
    fn foo(&mut self, x: i32, y: i32) {
         self.x = x;
         self.y = y;
    }
}

I think doing an impl<T> for Base<T> will produce a ton of copies of the same function, so composition isn't really an option.

I should probably point out that the implementation above was chosen for the simple reason that it allows a safer version of upcasting, which I need to do anyway.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
eugene2k
  • 121
  • 7
  • 9
    Rust doesn't even have classes. It uses a trait-based system for runtime polymorphism. Could you add an example of what exactly you want (maybe with working code written in C++)? Then we could tell you how one would write it in Rust :) – Lukas Kalbertodt Jun 18 '16 at 08:19
  • Edited the original post to be clearer. – eugene2k Jun 19 '16 at 17:37
  • 2
    As a C++ programmer, I feel the need to point out that the guideline is **Prefer Composition Over Inheritance** and your example is generally thought of as an anti-pattern. Inheritance should only be used to override behavior (aka, `virtual` functions), inheriting from non-polymorphic classes is generally a mistake (mixes two concept: is-a relationship and code re-use). Fortunately, Rust having the benefit of hindsight, there's no inheritance in Rust :) – Matthieu M. Jun 20 '16 at 07:37
  • I strongly disagree with the statement that inheritance should only be used to override behavior of virtual functions. Inheritance (or subtyping) allows the user of the derived class to call methods of the base class, without the need to implement an extra function in the derived class. – eugene2k Jun 20 '16 at 18:42

2 Answers2

3

Use composition instead of inheritance.

struct Base {
    x: u8,
    y: u8,
}

impl Base {
    fn foo(&mut self, x: u8, y: u8) {
        self.x = x;
        self.y = y;
    }
}

struct Derived {
    base: Base,
}

impl Derived {
    fn foo(&mut self, x: u8, y: u8) {
        self.base.foo(x, y);
    }
}

fn main() {
    let mut d = Derived { base: Base { x: 1, y: 3 } };
    d.foo(5, 6);
}

Rust could do a better job of making composition as easy on the keyboard as inheritance though. There is a RFC aiming to provide something better, as well as previous discussions 1, 2.

I think doing an impl<T> for Base<T> will produce a ton of copies of the same function, so composition isn't really an option.

I'm not sure what this syntax is trying to show as Base doesn't have a generic type. It is true that generic types are monomorphized for each concrete set of type parameters, but I don't see / know how that's any different from C++. I think that the Rust version will be slightly lighter-weight as you can't upcast from a Derived to a Base so the compiler doesn't have to maintain those invariants.

All of this assumes that there is some code to reuse. In many cases, you are better off programming to an interface, which is represented with traits in Rust. You can combine the two approaches — extract small reusable bits of code as components, bundle them together into aggregates, and implement traits for the aggregate types.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • It's possible to simplify this, when you have more than a couple derived classes. If you write a generic `struct Base` and add a `derived: T` to it, you don't have to write `base: Base` in each of the derived structs you wish to implement. You can then add impls for your derived structs using `impl Base`. I'm doing exactly that. The problem is what happens when you have a hundred derived structs? From my understanding for each Derived struct the compiler will generate a new function, that does the same thing. – eugene2k Jun 20 '16 at 18:32
  • I clarified the post again. Hope it makes more sense now. – eugene2k Jun 20 '16 at 18:55
0

I guess the answer is that there is no subtyping in Rust yet. The kinds of problems subtyping solves can't always be solved by composition, generics or traits. The issue is known to the devs and there have been solutions offered, but nothing has been decided yet.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
eugene2k
  • 121
  • 7