Last weekend I was in Berlin for the second Rust+GNOME Hackfest, kindly hosted at the Kinvolk office. This is in a great location, half a block away from the Kottbusser Tor station, right at the entrance of the trendy Kreuzberg neighborhood — full of interesting people, incredible graffitti, and good, diverse food.
My goals for the hackfest
Over the past weeks I had been converting gnome-class
from the old lalrpop-based parser into the new Procedural
Macros framework for Rust, or proc-macro2
for short. To do this the
parser for the gnome-class mini-language needs to be rewritten from
being specified in a lalrpop grammar, to using Rust's syn
crate.
Syn is a parser for Rust source code, written as a set of nom combinator parser macros. For gnome-class we want to extend the Rust language with a few conveniences to be able to specify GObject classes/subclasses, methods, signals, properties, interfaces, and all the goodies that GObject Introspection would expect.
During the hackfest, Alex Crichton, from the Rust core team, kindly took over my baby steps in compiler writing and made everything much more functional. It was invaluable to have him there to reason about macro hygiene (we are generating an unhygienic macro!), bugs in the quoting system, and general Rust-iness of the whole thing.
I was also able to talk to Sebastian Dröge about his work in writing GObjects in Rust by hand, for GStreamer, and what sort of things gnome-class could make easier. Sebastian knows GObject very well, and has been doing awesome work in making it easy to derive GObjects by hand in Rust, without lots of boilerplate — something with which gnome-class can certainly help.
I was also looking forward to talking again with Guillaume Gomez, one of the maintainers of gtk-rs, and who does so much work in the Rust ecosystem that I can't believe he has time for it all.
Extend the Rust language for GObject? Like Vala?
Yeah, pretty much.
Except that instead of a wholly new language, we use Rust as-is, and we just add syntactic constructs that make it easy to write GObjects without boilerplate. For example, this works right now:
#![feature(proc_macro)]
extern crate gobject_gen;
#[macro_use]
extern crate glib;
use gobject_gen::gobject_gen;
gobject_gen! {
// Derives from GObject
class One {
}
impl One {
// non-virtual method
pub fn one(&self) -> u32 {
1
}
virtual fn get(&self) -> u32 {
1
}
}
// Inherits from our other class
class Two: One {
}
impl One for Two {
// overrides the virtual method
// maybe we should use "override" instead of "virtual" here?
virtual fn get(&self) -> u32 {
2
}
}
}
#[test]
fn test() {
let one = One::new();
let two = Two::new();
assert!(one.one() == 1);
assert!(one.get() == 1);
assert!(two.one() == 1);
assert!(two.get() == 2);
}
This generates a little boatload of generated code,
including a good number of unsafe
calls to GObject functions
like g_type_register_static_simple()
. It also creates all the
traits and paraphernalia that Glib-rs would create for the Rust
binding of a normal GObject written in C.
The idea is that from the outside world, your generated GObject classes are indistinguishable from GObjects implemented in C.
The idea is to write GObject libraries in a better language than C, which can then be consumed from language bindings.
Current status of gnome-class
Up to about two weeks before the hackfest, the syntax for this mini-language was totally ad-hoc and limited. After a very productive discussion on the mailing list, we came up with a better syntax that definitely looks more Rust-like. It is also easier to implement, since the Rust parser in syn can be mostly reused as-is, or pruned down for the parts where we only support GObject-like methods, and not all the Rust bells and whistles (generics, lifetimes, trait bounds).
Gnome-class supports deriving classes directly from the basic GObject, or from other GObject subclasses in the style of glib-rs.
You can define virtual and non-virtual methods. You can override virtual methods from your superclasses.
Not all argument types are supported. In the end we should support argument types which are convertible from Rust to C types. We need to finish figuring out the annotations for ownership transfer of references.
We don't support GObject signals yet; I think that's my next task.
We don't support GObject properties yet.
We don't support defining new GType interfaces yet, but it is planned. It should be easy to support implementing existing interfaces, as it is pretty much the same as implementing a subclass.
The best way to see what works right now is probably to look at the examples, which also work as tests.
Digression on macro hygiene
Rust macros are hygienic, unlike C macros which work just through textual substitution. That is, names declared inside Rust macros will not clash with names in the calling code.
One peculiar thing about gnome-class is that the user gives us a few
names, like a class name Foo
and some things inside it, say, a
method name bar
, and a signal baz
and a property qux
.
From there we want to generate a bunch of boilerplate for GObject
registration and implementaiton. Some of the generated names in that
boilerplate would be
Foo // base name
FooClass // generated name for the class struct
Foo::bar() // A method
Foo::emit_baz() // Generated from the signal name
Foo::set_qux() // Generated property setter
foo_bar() // Generated C function for a method call
foo_get_type() // Generated C function that all GObjects have
However, if we want to actually generate those names inside our gnome-class macro and make them visible to the caller, we need to do so unhygienically. Alex started started a very interesting discussion on macro hygiene, so expect some news in the Rust world soon.
TL;DR: there is a difference between a code generator, which gnome-class mostly intends to be, and a macro system which is just an aid in typing repetitive code.
People for whom to to be thankful
During the hackfest, Nirbheek has been porting librsvg from Autotools to the Meson build system, and dealing with Rust peculiarities along the way. This is exactly what I needed! Thanks, Nirbheek!
Sebastian answered many of my questions about GObject internals and how to use them from the Rust side.
Zeeshan took us to a bunch of good restaurants. Korean, ramen, Greek, excellent pizza... My stomach is definitely thankful.
Berlin
I love Berlin. It is a cosmopolitan, progressive, LGBTQ-friendly city, with lots of things to do, vast distances to be traveled, with good public transport and bike lanes, diverse food to be eaten along the way...
But damnit, it's also cold at this time of the year. I don't think the weather was ever above 10°C while we were there, and mostly in a constant state of not-quite-rain. This is much different from the Berlin in the summer that I knew!
This is my third time visiting Berlin. The first one was during the Desktop Summit in 2011, and the second one was when my family and I visited the city two years ago. It is a city that I would definitely like to know better.
Thanks to the GNOME Foundation...
... for sponsoring my travel and accomodation during the hackfest.