Behavior inheritance in Rust

There is a common adage in Rust community that “Rust prefers composition over inheritance”. There is no inheritance support in common OO sense in Rust. You can achieve some OOP paradigms like, defining interface, using traits but not all.

Lack of behavior inheritance became obvious, when I started to think about building a GUI toolkit in Rust. I am familiar with GTK+ and Qt which use a model, where all GUI elements are composed in a tree consisting of objects sharing common “is-a” Widget interface and common default behavior. I’ve even built such a GUI toolkit at work. So I would like to try building similar toolkit in Rust.

Unfortunately the lack of behavior inheritance looked like a show-stopper. Traits can provide a default implementation, but cannot provide data fields this implementation can work on. Struct can have fields and implementation, but cannot be inherited from.

I raised the question on Bevy’s Discord and after a lengthy discussion we worked-out a satisfactory solution.
The pattern is similar to P-Impl – have an object which provides default implementation (fields and methods) and compose it into your own object. But instead of manually defining all proxy-methods, have a trait that provides them – this trait includes a method to get the base implementation object, also implementing the same trait.
The only thing you need to do in your own object to inherit on another, is to add a field with base object and trait implementation with one function returning reference to that field. You can also override all other methods, but this is optional – if you do not override anything, you will get the default behavior, like class MyButton extends Button {};

trait Widget {
fn base_widget(&mut self) -> &mut dyn Widget;
fn render(&mut self) {
self.base_widget().render();
}
}
struct BaseWidget;
impl Widget for BaseWidget {
fn base_widget(&mut self) -> &mut dyn Widget {
unreachable!("Attempt to base on self")
}
fn render(&mut self) {
println!("BaseWidget Render");
}
}
struct Button {
base_widget: BaseWidget
}
impl Widget for Button {
fn base_widget(&mut self) -> &mut dyn Widget {
&mut self.base_widget
}
}
fn main() {
let mut widget = Button {
base_widget: BaseWidget
};
widget.render();
}
view raw playground.rs hosted with ❤ by GitHub

It can be streamlined even more with a derive-like macro.
Even without a macro, one field and one method is not much of a boilerplate. It makes it obvious what is going on and allows easy extension/override.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s