Using Rust for Creating Ubuntu Touch Apps
Published 2019-01-18. Updated 2021-01-04.
This post aims to give you a small introduction to using the Rust programming language for writing apps for Ubuntu touch.
Context
Apps for Ubuntu touch usually have user interfaces written in Qt/QML or HTML. For QML there is the Ubuntu UI Toolkit available as well as a Suru style for QtQuick Controls 2. The Ubuntu UI Toolkit (or UUITK for short) is pretty specific to Ubuntu touch and was originally developed by Canonical. QtQuick Controls 2 (or QQC2 for short) is a Qt Quick module which is part of Qt since version 5.7 and also optimized for touch user interfaces.
In this post, we will utilize the latter to create a simple application. The interesting thing about this demo app however will not be the UI; instead we will focus on the backend.
There are already several options available to app developers when it comes to the choice of a backend for a Qt/QML app:
- C++: This is the default option for Qt applications
- Go: Utilizing this binding
- Python: Using PyOtherSide
Since QML has a rich JavaScript integration, some types of apps can also be written in pure QML and JavaScript.
This post introduces a new option: Rust.
About Rust
Rust is a relatively new programming language originally started by Graydon Hoare, fundend by Mozilla. It is a strongly statically typed multi-paradigm language that focuses on performance, reliability and productivity. Most notably, it is a memory-safe language that compiles to code with performance comparable to the likes of C and C++. If you want to learn more about Rust, I can highly recommend reading the great introductory book as well as looking at other resources for learning Rust.
Meet the Binding
There are already multiple bindings available for using Rust with Qt. The binding I am going to present today is called qmetaobject-rs. It is a fairly new project (first commit was on Jan 29, 2018) but already provides the most important building blocks for creating QML applications, so let's check it out!
Requirements
Before you get started you need to setup clickable, the go-to build tool for creating Ubuntu touch apps nowadays.
Hello World
cd
to your projects directory and run:
clickable create
It will present you with a list of templates you can use as a starting point. We will choose Rust
.
Clickable will now ask you for some metadata.
After that you should have a new directory named after the appname
you chose, e.g. rust-hello-word
cd rust-hello-world/
Great! Now we're ready to run our demo application on the phone using
clickable
or
clickable desktop
if you want to run it on the desktop.
You should see a little window pop up with a button that let's you display a greeting. The greeting of course is generated on the Rust side, serving as a simple demonstration and starting point.
The Code
That was easy. Now let's have a brief look at what is going on in the background.
Go ahead and open src/main.rs
with the text editor or IDE of your choice.
After some self-explanatory use
-statements you will find a Greeter
struct.
#[derive(QObject, Default)]
struct Greeter {
base : qt_base_class!(trait QObject),
name : qt_property!(QString; NOTIFY name_changed),
name_changed : qt_signal!(),
compute_greetings : qt_method!(fn compute_greetings(&self, verb : String) -> QString {
return (verb + " " + &self.name.to_string()).into()
})
}
Unsurprisingly, this is what generated the greeting when you pressed the demo button earlier.
The #[derive(QObject, Default)]
line decorating the struct is a Rust attribute. In this case it is a special attribute
that automatically derives behavior defined elsewhere.
Derive automatically creates a
trait implementation using Rust's powerful
macro-based meta-programming system.
The QObject
trait is defined in qmetaobject-rs
, the Default
trait is part of the Rust standard library.
Deriving from QObject
invokes most of the magic that qmetaobject-rs
provides to enable us using this Rust struct
like a usual QObject
in QML.
If you are curious about the details, I highly recommend to check out the announcement blog post for qmetaobject-rs.
qt_base_class!
, qt_property!
, qt_signal!
, and qt_method!
are all Rust macros.
The rest should be pretty self-explanatory if you are familiar with Qt.
Here is the main
function:
fn main() {
unsafe {
cpp! { {
#include <QtCore/QCoreApplication>
}}
cpp!{[]{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}}
}
QQuickStyle::set_style("Suru");
qrc::load();
qml_register_type::<Greeter>(cstr!("Greeter"), 1, 0, cstr!("Greeter"));
let mut engine = QmlEngine::new();
engine.load_file("qrc:/qml/Main.qml".into());
engine.exec();
}
Notice the unsafe
block. As of today, qmetaobject-rs
only wraps a small part of the Qt API which is most commonly
used when creating QML application.
Fortunately though, there is the handy cpp!
macro from the cpp crate which
makes embedding small snippets of C++ ridiculously easy in case you end up in a situation where you need such
non-exposed functionality.
The unsafe
block is required to opt-in to a
hand full of unsafe operations that Rust prevents by default
to fulfill its memory-safety guarantees.
Conclusion
That's it for this short introduction. Check out some of the resources linked in this post if you want to learn more and let me know if you have any questions or issues. Happy coding!