CXX-Qt Safe Rust Bindings for Qt
At KDAB, we have been investigating how to integrate Rust with Qt in a safe and idiomatic way. The solution we are currently working on is called CXX-Qt. It’s available in the GitHub and on crates.io. This blog post discusses the journey of CXX-Qt — where it started, where it can be used right now, and the future direction of the project. If you would like to get started with code examples, visit our Rust book.
Over the past few years, Rust has been gaining popularity in various sectors, from embedded devices to scalable web services. It brings the promise of performance, reliability, and productivity to every sector into which it has been introduced. In addition, Rust is useful from a security perspective due to its memory safety. For instance, a substantial amount of security issues in large-scale C or C++ projects tends to be memory-related. By using Rust, we reduce these kinds of issues while maintaining a level of performance that is on-par with other native languages, like C or C++, and far faster compared to most other memory-safe languages.
Where Would Rust Be Useful with Qt?
Typical Qt QML applications are composed of multiple parts:
- C++ plugins that provide models and represent the business logic
- QML, which defines the layout and components of the GUI
Rust has a rich ecosystem of libraries for (de)serialization, async futures, parsing unsafe inputs, threading, etc. The ability to integrate these Rust libraries into your Qt application makes a compelling story. Therefore, enhancing your C++ plugins and business logic is the most suitable use case for Rust. Due to QML’s fast iteration speed and flexibility, we recommend sticking with it for the layout and components of the GUI.
There are many techniques for combining Rust with Qt, each utilizing different ways to bind between Rust and Qt. We found that most solutions tend to have one or more of these problems:
- Direct bindings that are not idiomatic to Rust and only provide direct access to the C++ API
- Calls between C++ and Rust that are unsafe
- They don’t make use of Rust’s strong multi-threading guarantees or features
- They don’t have a license that suits the Rust ecosystem
- The attempt to use Rust for the whole application rather than just a plugin
- No code generation; developers need to manually define the bindings
Rather than simply providing bindings for the existing C++ API, CXX-Qt makes use of Qt’s strong object orientation and meta object system. The library allows you to define new QObject subclasses in Rust as a module. These can then be instantiated like any other QObject in QML, or even C++, if needed.
Every QObject defined by CXX-Qt is made up of two parts:
- A C++-based QObject instance that stores and exposes properties and invokable methods
- A Rust struct that implements any invokables, manages internal state, and handles change requests from properties or background threads
CXX-Qt then uses a library called CXX to communicate between Rust and C++. CXX is also being explored by large companies, such as Google, with autocxx. In comparison to normal bindings, CXX creates a bridge between Rust and C++ that is based on a safe subset of the two languages. By using procedural macros, we hide the details of the CXX bridge from you as a developer. We also provide a library of common Qt types that can cross the C++ <-> Rust bridge safely. We furthermore provide mechanisms for Rust background threads to notify and safely update the Qt state on the Qt thread.
All of this results in very idiomatic and safe Rust code that can seamlessly interact with existing Qt/C++ and Rust code.
The disadvantage of this approach is that we do not provide one-to-one bindings. Therefore, CXX-Qt needs to expose and implement every feature across the bridge. However, as the main focus of our solution is moving a C++ module to Rust, we believe that, for a majority of cases, we will be providing either a QObject or a model to QML and then exposing properties, invokables, and signals on these objects.
Current State and Future Direction
CXX-Qt is not production-ready or stable and is still a work-in-progress. We welcome any feedback on the direction of the project and features and/or any suggestions you may have.
Currently, the existing code is able to perform the following tasks:
- Define properties and invokables, exposing them onto a QObject
- Use common Qt types
- Listen for property changes or handle update requests from background Rust threads
In the future, we plan to add the following tasks/features:
- Other Qt base classes (such as QAbstractItemModel)
- Stabilize the API of macros and improve the API of Qt types
- Define and emit signals
- Expand the documentation to include tutorials and examples
Please feel free to share any suggestions or questions in the blog comments or, if you prefer, send us an email at email@example.com.