2

I am porting some code from C++ to Rust, and I'm unsure as to what the equivalent way to handle common base class members in C++ is in idiomatic Rust. An example of what I have in C++:

class Base {
public:
    Base(int n) : n_(n) {}
    virtual int foo() = 0;
    int n_;
};

class D1 : public Base {
public:
    D1(int n) : Base(n) {}
    virtual int foo() {/* return something to do with n_ */}

};

class D2 : public Base {
public:
    D1(int n) : Base(n) {}
    virtual int foo() {/* return some other thing to do with n_ */}

};

In C++, I can define n_ once in the base class constructor, and I can use it from any of the derived classes.

My Rust structs currently look like this:

struct D1 {
    n_: i32,
}

struct D2 {
    n_: i32,
}

trait Base {
    fn foo(&self) -> i32;
}

impl D1 {
    fn new(n: i32) -> Self {
        D1 { n_: n }
    }
}

impl D2 {
    fn new(n: i32) -> Self {
        D2 { n_: n }
    }
}

impl Base for D1 {
    fn foo(&self) -> i32 {
        // Contrived example to show D1 and D2 have different uses of n_
        self.n_
    }
}

impl Base for D2 {
    fn foo(&self) -> i32 {
        // Contrived example to show D1 and D2 have different uses of n_
        self.n_ * 2
    }
}

This feels like more code duplication than I'm used to. Is there a way to have an n_ data member declared only once for both structs?

avadali
  • 21
  • 3
  • *"how can I have them have a common data member?"* - what you have there does just that, or am I misunderstanding? – kmdreko Sep 28 '19 at 05:32
  • I know how to do this in C++, I don't know how to do this in Rust. My mistake for not clarifying – avadali Sep 28 '19 at 05:36
  • What's your actual Rust code? You can do this by either having your Rust `struct`s implement [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) or by creating a new trait that returns the value you want (and implementing this new trait for your `struct`s). – Cornstalks Sep 28 '19 at 05:40
  • I updated my question with an example of how I'm structuring my Rust code. – avadali Sep 28 '19 at 06:16
  • 2
    This is the kind of problem that isn't really solvable in the general case. The reason is because C++ uses inheritance for a *lot* of things -- data, behavior, interfaces -- and Rust doesn't have *one* solution for all those questions; it has *several* orthogonal features that may be composed. You have *some* problem that you have decided should be solved with inheritance, but that's not going to work in Rust; you need to discard the solution that works in C++ and build a new solution that works in Rust. – trent Sep 28 '19 at 12:54
  • For example, consider [making `Base` a generic struct and using `D1` and `D2` to parameterize it](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=44f36d36466997a19f816a39a3f9be42). Now you can write code that uses the `Base` interface, is generic over `D` and resolves at compile time. (Runtime dispatch is also possible.) This isn't necessarily appropriate for your problem, which is why we ask for the details of your *actual* problem, not just a sketch of how you *think* it should be solved. – trent Sep 28 '19 at 13:23
  • Thank you for the example and advice. My specific problem is that I have two device drivers for two versions of a module. Both drivers have the same interface and have to maintain some internal state (hence the need for data members inside the struct), but they interact with the physical module in different ways. – avadali Sep 28 '19 at 21:35
  • And does the program only ever use one of the drivers or may it use both at the same time? – trent Sep 29 '19 at 12:00
  • It may use both – avadali Sep 29 '19 at 23:45

0 Answers0