What this blog is about

In this blog, I’m going to post about the progress I make and the things that strike me during my Outreachy internship. Outreachy interns work on an open-source project for three months under the guidance of a mentor from the community. In my case the community is GNOME, my mentor is danigm, and the project is Fractal. Fractal is a pretty cool gtk-desktop application for real-time communication through the Matrix protocol. It’s written in Rust. Here’s a link: https://wiki.gnome.org/Apps/Fractal

The task

The goal of my internship is to implement a video player in Fractal. Right now receiving a message of a video attachment is handled the same way as receiving a pdf attachment: the functionalities Fractal provides for it are “open” (with another application) and “save”.

I’m going to integrate a video player into the Fractal application that allows the user to directly play the video inside the application. I’ll use GStreamer for that.

About the programming language: Rust

In order to ask for an Outreachy grant for a certain open-source project, applicants first have to contribute to that project for about a month. When choosing a project, I didn’t know any Rust. But the fact that Fractal is written in Rust was an important point in favor due to curiosity. But I also expected to have a hard time at the beginning. Fortunately, that wasn’t really the case. For those who haven’t used Rust, let me give two of the reasons why:

If you just start coding, the compiler takes you by the hand giving you advice like “You have done X. You can’t do that because of Y. Did you maybe mean to do Z?”. I took those pieces of advice as an opportunity to dig into the rules I had violated. That’s definitely a possible way to get a first grip on Rust.

Nevertheless, there are pretty good sources to learn the basics, for example, the Rust Book. Well, to be precise, there’s at least one (sorry, I’m a mathematician, can’t help it, I’ve only started reading that one so far). It’s not short, but it’s very fast to read and easy to understand. In my opinion, the only exception being the topics on lifetimes. But lifetimes can still be understood by other means.

About the GUI library: GTK-rs

The GUI library Fractal uses is GTK-rs, a wrapper in Rust around the C-library GTK. One random interesting fact about GTK-rs, that called my attention at some point reading the Fractal code, was the following. Based on GObject, GTK uses inheritance structures. For example, the class Label is a subclass of Widget. In Rust there aren’t classes. Label is a type and Widget is so as well. So how does Label inherit from Widget in GTK-rs? Well, strictly speaking, it doesn’t. But both types implement a trait called Cast (in Haskell jargon: they are of type class Cast). In fact, any type in GTK-rs coming from GObject implements Cast. The Cast trait allows a type to be converted to types corresponding to superclasses (or subclasses, when that makes sense) in the GObject tree. That’s how you can convert a label to a widget, call a Widget method on it, and -if you want- convert it back.

Converting an instance of a type to a type corresponding to a super- or subclass (in the GObject logic) is called upcast or downcast, respectively. But how does GTK-rs capture the subclass/superclass logic, if the concept of classes doesn’t exist? The answer is: via the trait IsA<T> (here, T is a type parameter). If a type corresponds to a GObject subclass of another type P, then it implements the trait IsA<P> (here, P is a concrete type: the one corresponding to the superclass). For example, Label implements the trait IsA<Widget>. Of course, Widget has far more subclasses than just Label. All of them implement the trait IsA<Widget>

Now, let me come back to the end of the penultimate paragraph and explain why the Cast trait allows a label to be upcasted to a widget. By definition of the trait Cast, saying that Label implements Cast means that it has to have one method upcast<P> for every type P for which it implements IsA<P>. So it has to have a method upcast<Widget>. That method converts a label into a widget.

Downcasting methods are guaranteed very similarly. To start with, whenever a type T implements the trait IsA<P> for some type P (i.e. P corresponds to a GObject superclass of T), the type P implements the trait CanDowncast<T>. Therefore, Widget implements CanDowncast<Label>. Again by definition of the trait Cast, that Widget implements Cast means that it has to have one method downcast<T> for every type T for which it implements CanDowncast<T>. So it has to have a method downcast<Label>. Notice that a widget can only be downcasted to a label, if it comes from a label in the first place. That is captured by the fact that the return type of downcast<Label> on Widget is Result<Label, Widget>.

Of course, if it wasn’t for wrapping around an object- and inheritance-oriented library, one might directly work in a different mindset in Rust. But it’s interesting to see the tricks that have been used to realize this GObject mindset in Rust.

Create your website at WordPress.com
Get started