How to use C++ polymorphism in Rust

Senges Alex
The Startup
Published in
4 min readJun 21, 2020

--

You can find the source code for this article on Github: https://github.com/sakex/Rust-CPP-FFI-Polymorphism.

A rather common C++ library design pattern is to create an abstract class that the users will have to inherit from to implement their business logic. The concrete class’ instances are then passed to a function or another class provided by the library that will run some logic on the instances.

For instance, we could imagine a GUI library that would provide an AbstractButton class that the users would inherit from to create their own buttons. Something like:

Then the user would inherit from that class that way:

To use the GUI engine with our classes, one would need to write:

Note here that Engine manages the memory of the buttons itself.

It is a very powerful design pattern, and it is used by many libraries. Unfortunately, it cannot be exported as is to other languages using Foreign Function Interfaces (FFIs) because other languages have no idea about vtables and C++’s non stable ABI.

In this article, I will show you how I managed to export our C++ polymorphic classes to Rust for a personal project and how I used traits to maintain the polymorphic aspect of the code.

What we want in Rust

In the end we will want to be able to use our C++ class in Rust with polymorphism like in the original version. The best way to achieve polymorphism in Rust is through traits and generics. We will want to do something like this:

Export the classes from C++ to Rust

First things first, we want to export our classes so they can be used by any language. Most programming languages can interface directly with C. Therefore it has become the de facto lowest common denominator between languages.

To export C++ classes to C, we have to wrap calls to our C++ members and classes in the extern “C” specifier. In our example, we want to export the engine class as well as the AbstractButton to use them in Rust.

Engine

First of all, let’s export the code to create an Engine:

It is easier to allocate it on the heap, otherwise we would have needed to write the names and the types of all the members in Rust too (recursively). Instead, we can just use the pointer in Rust as a void pointer. We will have to remember to manage memory manually later:

Let’s write a wrapper so that the end user of the Rust library won’t have to write unsafe code and worry about memory allocation details:

Edit: libc::free will NOT call the C++ destructor just like free(ptr) doesn’t. The real way to do it would be to wrap a call to delete in a free function and export it to Rust. I didn’t do it here to not add more space to an already long article.

Now let’s write bindings for the abstract class

Remember that we can’t use class methods or inheritance in C. A common way to mimic that is to use a struct with function pointers and a void pointer to a “context” object on which we we call the functions:

Now, we can’t just pass that struct to “Engine::registerButton()”, so we also need a class that inherits from “AbstractButton” and owns a pointer to an “AbstractButtonBinding” binding.

This class’ job is to call the function pointers of “AbstractButtonBinding” in the inherited pure virtual methods.

The last piece of C++ code we need to write is a wrapper for “Engine::registerButton()”:

It takes a pointer to a “Engine” instance and a pointer to an “AbstractButtonBinding” instance, it creates a new instance of a “ButtonBindingConcrete” and passes it to “Engine::registerButton()”.

Rust side

First we want to replicate the “AbstractButtonBinding” in Rust. But remember, we want our users to implement the “Button” trait:

Rust allows us to use generic types in place of void pointers:

We also want to bind the “register_button” free function:

Now the hard part

All the bindings are written. We go back to our “Engine” struct and implement the Rust’s side of “Engine::register_button()”. register_button takes an arbitrary struct that implements Button. The button has to be allocated on the heap so we pass it a Box<T: Button>.

Rust allows us to define functions inside other functions and even mark them as generic and extern “C”. So we define our functions “click” and “inner_text” inside “register_button”. We have to make sure our types follow the C ABI and we allocate all the pointers on the heap, otherwise we would have dangling pointers to the stack.

Let’s use our library

I added a small .click() function to test it was working. You can check it in the Github repository https://github.com/sakex/Rust-CPP-FFI-Polymorphism

The output is:

Count: 1
Count: 21
Count: 2
Count: 22
Count: 3
Count: 23

Compiling

Compiling is very easy thanks to the “cc” crate. Just write a build.rs file:

Note that I used separate compilation instead of the header only files in the examples for the sake of reducing space.

cargo.toml

[package]
name = "medium"
version = "0.1.0"
authors = ["sakex <alexandre@senges.ch>"]
edition = "2018"


[dependencies]
libc = "0.2"

[build-dependencies]
cc = "1.0"

Execute “cargo run” and you are good to go

Conclusion

Thanks for reading, it ended to be way longer than I expected at first. It is my first article on Medium, so feel free to tell me what I should change and if you would like to read more about Rust and C++ use cases.

--

--