Librsvg is almost rustified now

- Tags: gnome, librsvg, rust

Since a few days ago, librsvg's library implementation is almost 100% Rust code. Paolo Borelli's and Carlos Martín Nieto's latest commits made it possible.

What does "almost 100% Rust code" mean here?

  • The C code no longer has struct fields that refer to the library's real work. The only field in RsvgHandlePrivate is an opaque pointer to a Rust-side structure. All the rest of the library's data lives in Rust structs.

  • The public API is implemented in C, but it is just stubs that immediately call into Rust functions. For example:

gboolean
rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
{
    g_return_val_if_fail (RSVG_IS_HANDLE (handle), FALSE);
    g_return_val_if_fail (cr != NULL, FALSE);

    return rsvg_handle_rust_render_cairo_sub (handle, cr, id);
}
  • The GObject boilerplate and supporting code is still in C: rsvg_handle_class_init and set_property and friends.

  • All the high-level tests are still done in C.

  • The gdk-pixbuf loader for SVG files is done in C.

Someone posted a chart on Reddit about the rustification of librsvg, comparing lines of code in each language vs. time.

Rustifying the remaining C code

There is only a handful of very small functions from the public API still implemented in C, and I am converting them one by one to Rust. These are just helper functions built on top of other public API that does the real work.

Converting the gdk-pixbuf loader to Rust seems like writing a little glue code for the loadable module; the actual loading is just a couple of calls to librsvg's API.

Rsvg-rs in rsvg?

Converting the tests to Rust... ideally this would use the rsvg-rs bindings; for example, it is what I already use for rsvg-bench, a benchmarking program for librsvg.

I have an unfinished branch to merge the rsvg-rs repository into librsvg's own repository. This is because...

  1. Librsvg builds its library, librsvg.so
  2. Gobject-introspection runs on librsvg.so and the source code, and produces librsvg.gir
  3. Rsvg-rs's build system calls gir on librsvg.gir to generate the Rust binding's code.

As you can imagine, doing all of this with Autotools is... rather convoluted. It gives me a lot of anxiety to think that there is also an unfinished branch to port the build system to Meson, where probably doing the .so→.gir→rs chain would be easier, but who knows. Help in this area is much appreciated!

An alternative?

Rustified tests could, of course, call the C API of librsvg by hand, in unsafe code. This may not be idiomatic, but sounds like it could be done relatively quickly.

Future work

There are two options to get rid of all the C code in the library, and just leave C header files for public consumption:

  1. Do the GObject implementation in Rust, using Sebastian Dröge's work from GStreamer to do this easily.

  2. Work on making gnome-class powerful enough to implement the librsvg API directly, and in an ABI-compatible fashion to what there is right now.

The second case will probably build upon the first one, since one of my plans for gnome-class is to make it generate code that uses Sebastian's, instead of generating all the GObject boilerplate by hand.