During the GNOME+Rust hackfest in Mexico City, Niko Matsakis
started the implementation of gnome-class, a procedural macro
that will let people implement new GObject classes in Rust and export
them to the world. Currently, if you want to write a new GObject
(e.g. a new widget) and put it in a library so that it can be used
from language bindings via GObject-Introspection, you have to do it in
C. It would be nice to be able to do this in a safe language like
Rust.

How would it be done by hand?

In a C implementation of a new GObject subclass, one calls things like
g_type_register_static() and g_signal_new() by hand, while being
careful to specify the correct GType for each value, and being
super-careful about everything, as C demands.

In Rust, one can in fact do exactly the same thing. You can call the
same, low-level GObject and GType functions. You can use
#[repr(C)]] for the instance and class structs that GObject will
allocate for you, and which you then fill in.

How would it be done by a machine?

That's what Niko's gnome-class is about. During the hackfest it
got to the point of being able to generate the code to create a new
GObject subclass, register it, and export functions for methods. The
syntax is not finalized yet, but it looks something like this:

I started adding support for declaring GObject signals — mainly being
able to parse them from what goes inside gobject_gen!() — and then
being able to call g_signal_newv() at the appropriate time during
the class_init() implementation.

Types in signals

Creating a signal for a GObject class is basically like specifying a
function prototype: the object will invoke a callback function with
certain arguments and return value when the signal is emitted. For
example, this is how GtkButton registers its button-press-event
signal:

button_press_event_id=g_signal_new(I_("button-press-event"),...G_TYPE_BOOLEAN,/* type of return value */1,/* how many arguments? */GDK_TYPE_EVENT);/* type of first and only argument */

g_signal_new() creates the signal and returns a signal id, an
integer. Later, when the object wants to emit the signal, it uses
that signal id like this:

In the nice gobject_gen!() macro, if I am going to have a signal
declaration like

signalbutton_press_event(&self,event: &ButtonPressEvent)-> bool;

then I will need to be able to translate the type names for
ButtonPressEvent and bool into something that g_signal_newv() will
understand: I need the GType values for those. Fundamental
types like gboolean get constants like G_TYPE_BOOLEAN. Types
that are defined at runtime, like GDK_TYPE_EVENT, get GType values
generated at runtime, too, when one registers the type with
g_type_register_*().

Glib types in Rust

Going from Glib to Rust

First we need a way to convert Glib's types to Rust, and vice-versa.
There is a trait to convert simple Glib types into Rust types:

pubtraitFromGlib<T>: Sized{fnfrom_glib(val: T)-> Self;}

This means, if I have a T which is a Glib type, this trait will give
you a from_glib() function which will convert it to a Rust type
which is Sized, i.e. a type whose size is known at compilation time.

Booleans in glib and Rust have different sizes, and also
different values. Glib's booleans use the C convention: 0 is false
and anything else is true, while in Rust booleans are strictly false
or true, and the size is undefined (with the current Rust ABI, it's
one byte).

Going from Rust to Glib

And to go the other way around, from a Rust bool to a gboolean?
There is this trait:

pubtraitToGlib{typeGlibType;fnto_glib(&self)-> Self::GlibType;}

This means, if you have a Rust type that maps to a corresponding
GlibType, this will give you a to_glib() function to do the
conversion.

(If you are thinking "a function call to marshal a boolean" — note how
the functions are inlined, and the optimizer basically compiles them
down to nothing.)

Pointer types - from Glib to Rust

That's all very nice for simple types like booleans and ints.
Pointers to other objects are slightly more complicated.

GObject-Introspection allows one to specify how pointer arguments to
functions are handled by using a transfer specifier.

(transfer none)

For example, if you call gtk_window_set_title(window, "Hello"), you
would expect the function to make its own copy of the "Hello"
string. In Rust terms, you would be passing it a simple borrowed
reference. GObject-Introspection (we'll abbreviate it as GI) calls
this GI_TRANSFER_NOTHING, and it's specified by using
(transfer none) in the documentation strings for function arguments
or return values.

The corresponding trait to bring in pointers from Glib to Rust,
without taking ownership, is this. It's unsafe because it will be
used to de-reference pointers that come from the wild west:

Line 4: Do the conversion with from_glib_none() with the trait we
saw before, put it in res.

Line 5: Call g_free() on the original C string.

Line 6: Return the res, a Rust string which we own.

Pointer types - from Rust to Glib

Consider the case where you want to pass a String from Rust to a Glib function
that takes a *const c_char — in C parlance, a char *, without the
Glib function acquiring ownership of the string. For example, assume
that the C version of gtk_window_set_title() is in the gtk_ffi
module. You may want to call it like this:

Line 2: Build a CString like we way a few days ago: this
allocates a byte buffer with space for a nul terminator, and copies
the string's bytes. We unwrap() for this simple example, because
CString::new() will return an error if the String contained nul
characters in the middle of the string, which C doesn't understand.

Line 3: Call into_raw() to get a pointer to the byte buffer, and
cast it to a *const c_char. We'll need to free this value later.

But this kind of sucks, because we the have to use this function, pass
the pointer to a C function, and then reconstitute the CString so it
can free the byte buffer:

The solution that Glib-rs provides for this is very Rusty, and rather
elegant.

Stashes

We want:

A temporary place to put a piece of data

A pointer to that buffer

Automatic memory management for both of those

Glib-rs defines a Stash for this:

1
2
3
4
5
6

pubstructStash<'a,// we have a lifetimeP: Copy,// the pointer must be copy-ableT: ?Sized+ToGlibPtr<'a,P>>(// Type for the temporary placepubP,// We store a pointer...pub<TasToGlibPtr<'a,P>>::Storage// ... to a piece of data with that lifetime ...);

... and the piece of data must be of of the associated typeToGlibPtr::Storage, which we will see shortly.

This struct Stash goes along with the ToGlibPtr trait:

pubtraitToGlibPtr<'a,P: Copy>{typeStorage;fnto_glib_none(&'aself)-> Stash<'a,P,Self>;// returns a Stash whose temporary storage// has the lifetime of our original data}

Let's unpack this by looking at the implementation of the "transfer a
String to a C function while keeping ownership":

Now Rust knows that the temporary buffer inside the Stash has the lifetime of
my_string, and it will free it automatically when the string goes
out of scope. If we can accept the .to_glib_none().0 incantation
for "lending" pointers to C, this works perfectly.

(transfer full)

And for transferring ownership to the C function? The ToGlibPtr
trait has another method: