Using Rust for Creating Ubuntu Touch Apps

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:

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

You need to setup two things on your development machine to get started:

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 - Rust/QML App.

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/

Because Clickable is reusing the Rust installation on your host machine for the Cargo registry, you will now need to set the CARGO_HOME environment variable so that clickable will find your installation.

# ~/.cargo is the default path
export CARGO_HOME=~/.cargo/

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!