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.
Why Rust?
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
- JavaScript, for scripting in QML
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.
Existing Solutions
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
Our Solution
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
If you would like to contribute, the code is available at https://github.com/KDAB/cxx-qt and documentation can be found at https://kdab.github.io/cxx-qt/book.
Please feel free to share any suggestions or questions in the blog comments or, if you prefer, send us an email at info@kdab.com.
Rust training course
In collaboration with Ferrous Systems we now offer an “Introduction to Rust” training course. Learn how to write safe concurrent programs, leverage error handling, and use Rust’s documentation for research and troubleshooting. Find out more…
If you like this article and want to read similar material, consider subscribing via our RSS feed.
Subscribe to KDAB TV for similar informative short video content.
KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.
While I’m always happy to see more interest in integrating Rust and Qt, if you’re so focused on QML, I’m puzzled why you didn’t join forces with the existing QMetaObject crate. I don’t see any mention of it here.
Hi Stephan,
Thanks for the comment!
We did investigate qmetaobject-rs; however, we felt there were a few problems with it. As the question of comparisons to other attempts keeps coming up, I think we will add a section in the book comparing the differences since they all have their own advantages and disadvantages.
Some of the differences between CXX-Qt and qmetaobject-rs are use of procedural attribute macro rather than derive macros allowing for more idiomatic Rust code (we can integrate well later with other Rust libraries like serde), using CXX rather than cpp internally to reduce unsafe code and improve maintainability, clear multi-threading story, and exposing API as a bridge rather than direct API to match Qt’s (we see this as an advantage for our purposes, others may see this as a disadvantage for theirs as you can’t call Qt API directly) etc. If we add a section in the book then we’ll try and explain the differences and reasons in more detail.
Thanks,
Andrew