5
votes

I'm trying to implement something in Rust that works like a C++ virtual function in a class, I would have a base struct with data, then I would keep some functions undefined, like the following example:

class A {
    int stuff;
public:
    virtual void foo(int a, int b) = 0;
    void function_that_calls_foo() { /*...*/ foo(1, 2); /*...*/ }
}

class B: public A { void foo(int a, int b) { /* ... */ } }

I was trying to implement it using function pointers, but without much success. I could use a trait with A's functions, and implement A on the other class, but I would lose the struct's data. What's the best (fastest?) way to implement this kind of thing in Rust?

struct A {
    ...
}

impl A {
    fn function_that_calls_foo(&self) {
        ...
        self.foo(a, b);
        ...
    }
}

struct B {
    a: A;
}

impl B {
    fn xxx(&self) {
        a.function_that_calls_foo(1, 2);
    }

    fn foo(&self, a: i32, b: i32) {...}
}
1
You're looking for trait objects.E_net4 the voter
So, Rust is very much Composition Over Inheritance.Matthieu M.
These kinds of questions, "How do I do class inheritance in Rust?" are usually solved best by taking a step back, looking at the actual problem you're trying to solve (the real problem, not a fake problem with foo and xxx for function names) and looking at your new toolbox. If you try to translate class inheritance directly into Rust code, you'll figure out a way to make it work, but the code may be very clunky compared to more idiomatic Rust code.Dietrich Epp

1 Answers

7
votes

keep some functions undefined

I'm adding the implicit "and have some functions that call that to-be-defined function".

As E_net4 says, use a trait:

trait Foo {
    fn foo(&self, a: i32, b: i32) -> i32;

    fn function_that_calls_foo(&self) {
        println!("{}", self.foo(1, 2));
    }
}

You can then implement the trait for Base:

struct Base {
    stuff: i32,
}

impl Foo for Base {
    fn foo(&self, a: i32, b: i32) -> i32 {
        self.stuff + a + b
    }
}

And as Matthieu M. says, Rust doesn't have inheritance, so use composition:

struct Base {
    stuff: i32,
}

impl Base {
    fn reusable(&self) -> i32 {
        self.stuff + 1
    }
}

struct Alpha {
    base: Base,
    modifier: i32,
}

impl Foo for Alpha {
    fn foo(&self, a: i32, b: i32) -> i32 {
        (self.base.reusable() + a + b) * self.modifier
    }
}

You can combine the two concepts as well, by taking a generic that is constrained by a type parameter.


I'll strongly second Dietrich Epp's point. Using a new language should involve checking out new paradigms. Inheritance for the purposes of code reuse is not usually a great idea, even in languages that support it. Instead, create smaller building blocks and combine them together.