Handling generation errors

There are a few kinds of errors (not much luckily) which can happen with gir generation. Let's take a look at them.

Cannot find macros

Compilation of the generated bindings may fail with errors like the following:

error: cannot find macro `skip_assert_initialized` in this scope
  --> src/auto/enums.rs:83:9
   |
83 |         skip_assert_initialized!();
   |         ^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: have you added the `#[macro_use]` on the module/import?

error: cannot find macro `assert_initialized_main_thread` in this scope
  --> src/auto/example.rs:33:9
   |
33 |         assert_initialized_main_thread!();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: have you added the `#[macro_use]` on the module/import?

In this case you’ll have to implement them yourself. For example, you can add the following to your lib.rs file:

#![allow(unused)]
fn main() {
/// No-op.
macro_rules! skip_assert_initialized {
    () => {};
}

/// Asserts that this is the main thread and either `gdk::init` or `gtk::init` has been called.
macro_rules! assert_initialized_main_thread {
    () => {
        if !::gtk::is_initialized_main_thread() {
            if ::gtk::is_initialized() {
                panic!("GTK may only be used from the main thread.");
            } else {
                panic!("GTK has not been initialized. Call `gtk::init` first.");
            }
        }
    };
}
}

Note: macros are order-dependent and you must insert this code before declaring modules that use it (e.g. mod auto).

One complication here is that the assert_initialized_main_thread! macro depends on the exact library. If it's GTK-based then the above macro is likely correct, unless the library has its own initialization function. If it has its own initialization function it would need to be handled in addition to GTK's here in the same way.

For non-GTK-based libraries this macro would handle the initialization function of that library in the same way, or if there is none would simply do nothing:

#![allow(unused)]
fn main() {
/// No-op.
macro_rules! assert_initialized_main_thread {
    () => {};
}
}

Missing memory management functions

If gir generation fails (for whatever reason), it means you'll have to implement the type yourself. Just like types from other gtk-rs crates, you'll need to put it into the "manual" list. Then you need to put the type into the src folder (or inside a subfolder, you know how Rust works).

/!\ Don't forget to reexport the type inside your src/lib.rs file! For example, let's take a look at the requisition.rs file from the gtk crate.

Since it's a "simple" type (no pointer, therefore no memory management to do), gir doesn't know how to generate it. You'll need to implement some traits by hand like ToGlibPtr or ToGlibPtrMut (depending on your needs).

Bad function generation

In some cases, the generated code isn't correct (array parameters are often an issue). In such cases, it's better to just make the implementation yourself. As an example, let's say you want to implement Region::is_empty yourself. A few changes have to be made. Let's start with Gir.toml:

generate = [
    "GtkSource.Language",
]

[[object]]
name = "GtkSource.Region"
status = "generate"
    [[object.function]]
    name = "is_empty"
    ignore = true

So to sum up what I wrote above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. Then we say to gir that it should generate (through status = "generate"). However, we also tell it that we don't want the "is_empty" to be generated.

Now that we've done that, we need to reimplement it. Let's create a src/region.rs file:

#![allow(unused)]
fn main() {
use glib::object::IsA;
use glib::translate::*;
use Region;

pub trait RegionExtManual: 'static {
    pub fn is_empty(&self) -> bool;
}

impl<O: IsA<Region>> RegionExtManual for O {
    pub fn is_empty(&self) -> bool {
        // blablabla
        true
    }
}
}

You might wonder: "why not just implementing it on the Region type directly?". Because like this, a subclass will also be able to use this trait easily as long as it implements IsA<Region>. For instance, in gtk, everything that implements IsA<Widget> (so almost every GTK types) can use those methods.

As usual, don't forget to reexport the trait. A little tip about reexporting manual traits: in gtk-rs, we create a src/prelude.rs file which reexports all traits (both manual and generated ones), making it simpler for users to use them through use [DEPENDENCY]::prelude::*. It looks like this:

#![allow(unused)]
fn main() {
pub use auto::traits::*;

pub use region::RegionExtManual;
}

Then it's reexported as follows from the src/lib.rs file:

#![allow(unused)]
fn main() {
pub mod prelude;

pub use prelude::*;
}