In the first part, we saw how glib-rs provides
the FromGlib and ToGlib traits to let Rust
code convert from/to Glib's simple types, like to convert from a Glib
gboolean to a Rust bool and vice-versa. We also saw the special
needs of strings; since they are passed by reference and are not
copied as simple values, we can use
FromGlibPtrNone and
FromGlibPtrFull depending on what kind of
ownership transfer we want, none for "just make it look like we are
using a borrowed reference", or full for "I'll take over the data and
free it when I'm done". Going the other way around, we can use
ToGlibPtr and its methods to pass things from Rust to
Glib.

In this part, we'll see the tools that glib-rs provides to do
conversions of more complex data types. We'll look at two cases:

Passing arrays from Glib to Rust

We'll look at the case for transferring null-terminated arrays of
strings, since it's an interesting one. There are other traits to
convert from Glib arrays whose length is known, not implied with a
NULL element, but for now we'll only look at arrays of strings.

Null-terminated arrays of strings

Look at this function for GtkAboutDialog:

/** * gtk_about_dialog_add_credit_section: * @about: A #GtkAboutDialog * @section_name: The name of the section * @people: (array zero-terminated=1): The people who belong to that section * ... */voidgtk_about_dialog_add_credit_section(GtkAboutDialog*about,constgchar*section_name,constgchar**people)

The function expects an array of gchar *, where the last element is
a NULL. Instead of passing an explicit length for the array, it's
done implicitly by requiring a NULL pointer after the last element.
The gtk-doc annotation says (array zero-terminated=1). When we
generate information for the GObject-Introspection Repository (GIR),
this is what comes out:

1
2
3
4
5
6
7
8
9
10

<methodname="add_credit_section"c:identifier="gtk_about_dialog_add_credit_section"version="3.4">
..
<parametername="people"transfer-ownership="none"><docxml:space="preserve">The people who belong to that section</doc><arrayc:type="gchar**"><typename="utf8"c:type="gchar*"/></array></parameter>

You can see the transfer-ownership="none" in line 5. This means
that the function will not take ownership of the passed array; it will
make its own copy instead. By convention, GIR assumes that arrays of
strings are NULL-terminated, so there is no special annotation for
that here. If we were implementing this function in Rust, how would we
read that C array of UTF-8 strings and turn it into a Rust
Vec<String> or something? Easy:

letc_char_array: *mut*mutc_char=...;// comes from Glibletrust_translators=FromGlibPtrContainer::from_glib_none(c_char_array);// rust_translators is a Vec<String>

Let's look at how this bad boy is implemented.

First stage: impl FromGlibPtrContainer for Vec<T>

We want to go from a "*mut *mut c_char" (in C parlance, a "gchar **")
to a Vec<String>. Indeed, there is an implementation of the
FromGlibPtrContainer trait for Vecs
here. These are the first few lines:

The impl declaration is pretty horrible, so just look at the
method: from_glib_none_as_vec() takes in a GList, then calls
g_list_length() on it, and finally calls
FromGlibContainer::from_glib_none_num() with the length it computed.

Again, ignore the horrible impl declaration and just look at
from_glib_none_num_as_vec().

Line 4: that function takes in a ptr to a GList, and a num with
the list's length, which we already computed above.

Line 5: Return an empty vector if we have an empty list.

Line 8: Allocate a vector of suitable capacity.

Line 9: For each element, convert it with from_glib_none() and push
it to the array.

Line 14: Walk to the next element in the list.

Passing containers from Rust to Glib

This post is getting a bit long, so I'll just mention this briefly.
There is a trait ToGlibContainerFromSlice that takes a Rust slice,
and can convert it to various Glib types.

To GSlist and GList. These have
methods like to_glib_none_from_slice() and
to_glib_full_from_slice()

To an array of fundamental types. Here, you can choose
between to_glib_none_from_slice(), which gives you a Stash like
we saw the last time. Or, you can use
to_glib_full_from_slice(), which gives you back a g_malloc()ed
array with copied items. Finally, to_glib_container_from_slice()
gives you back a g_malloc()ed array of pointers to values rather
than plain values themselves. Which function you choose depends on
which C API you want to call.

I hope this post gives you enough practice to be able to "follow the
traits" for each of those if you want to look at the implementations.