Hello everyone, time for a new release!

This time, this is mostly about stabilization and simplification. It means that gtk-rs is now simpler to use and more complete than ever. Let’s take a tour of what’s new(er)!

Macro to make gtk-rs usage far simpler §

A big productivity killer when using gtk-rs in the past was the requirement to pass cloned references to objects, or even worse, weak references into signal handler closures. This is still required but to make it more ergonomic, a new clone! macro is provided as part of glib.

See the documentation for various examples on how to use it. The big advantage of the macro is that you don’t have to manually declare new local variables with a different name that are then moved into the closure, but simply have to provide the name of the variable you want to make available in the closure and whether it should be passed as a strong or weak reference. Inside the closure it can then be used as-is and for example upgrading any weak references manually is not necessary. In case of failure of upgrading a weak reference, an optional default return value for the closure can be provided or the closure can be configured to panic.

The macro works on any glib::Object as well as on any Arc and Rc.

As a side-note, it is important to know the difference between strong and weak references and not simply clone (strong reference) everything. By using strong references everywhere, many GTK applications (not only in Rust) currently create reference cycles and therefore have memory leaks. If, for example, you want to pass a reference to your Window into a Button’s clicked signal, you would create a reference cycle between the window, button, signal handler and again the window. This would lead to the whole cycle to be kept alive forever and leaked. The solution to this is to make one of the references a weak reference, in this case the reference to the window that is passed into the clicked signal handler. See also the Rc documentation about reference cycles.

Subclass §

The “subclass” cargo feature was removed from all crates and is instead enabled by default with this release. The GObject subclassing support matured a lot over the last months and is ready for wider usage. A basic example for subclassing gtk::Application and gtk::ApplicationWindow can be found here, another example using custom glib::Object subclasses as part of a gtk::ListBox model can be found here and various examples for creating GStreamer elements here.

While there are still subclassing bindings missing for many types, various basic types in the gio, gtk and gstreamer crates are covered already. If something is missing for you, please let us know with an issue or, even better, a pull request.

Thanks to subclassing being a first-class citizen of the bindings, there is also an adapter available for making any std::io::Read available as gio::InputStream and any std::io::Write as gio::OutputStream: gio::ReadInputStream and gio::WriteOutputStream. Adapters in the other direction are available as gio::InputStream::into_read() and gio::OutputStream::into_write().

Futures §

The futures support was ported to std Futures and futures 0.3, and as async/await is stabilized now it was also enabled by default. The “futures” feature is not needed anymore.

With the futures support in gio and other modules it is now possible to write applications making use of asynchronous I/O with async/await, which allows writing asynchronous code in a much simpler way that looks close to the equivalent synchronous code. Check async/await stable on the official Rust blog for more details.

An example making use of this with gio’s asynchronous file reading support can be found here. While it is not as streamlined as with native Rust crates like async-std or tokio because of how the gio API works, it nonetheless much more convenient to work with than the previous (but still available) callback-based approach.

Another example that shows integration of gio with generic Rust futures crates can be found here . Each gio::PollableInputStream and gio::PollableOutputStream can be converted into an AsyncRead / AsyncWrite, and by this allows integration with the wider Rust async ecosystem. In this case a gio TCP connection is wrapped in a TLS connection provided by the async-tls crate, which uses rustls as underlying TLS implementation.

GTK4 §

We have initial GTK4 bindings now, which are the result of @sfanxiang’s GSoC project this year. While not part of this release because GTK4 itself is still not API stable, you can also try it from git. The GTK4 bindings can be found here. Once there are release candidates of GTK4 we will also do alpha releases of the bindings.

Cairo improvements §

The cairo bindings now consistently return Results for various functions instead of sometimes Options, sometimes silently failing. Many cairo operations return an actual Surface in an error state if something goes wrong, and this surface will then (usually silently) fail any future operations on it. Instead of returning the surface, an Err is returned now as it should.

GTK Builder improvements §

In gtk::Builder UI description files it is possible to declare signal handlers for the widgets. While it’s not possible to connect them automatically to functions in Rust in a safe way, it is now possible for applications to implement the connection logic themselves based on the information from the UI description. gtk::Builder::connect_signals_full() allows to provide closures for each signal handler name that is given in the UI description.

glib::Value::get improvements §

glib::Value::get() was changed to allow distinguishing between the value containing a None and trying to get a value of the wrong type out of it. This means that it now returns a Result, and also that for types that can’t possibly be None (e.g. integer types), Value::get_some() is provided as a helper to not require unwrapping the returned Option from the normal Value::get().

That’s it for biggest changes. A lot of other small ones are in as well. So enjoy!

Changes §

For the interested ones, here is the list of the merged pull requests:

sys:

glib:

cairo:

sourceview:

atk:

gio:

pango:

gdk-pixbuf:

gdk:

gtk:

pangocairo:

gtk-test:

All this was possible thanks to the gtk-rs/gir project as well:

Thanks to all of our contributors for their (awesome!) work on this release: