How to disconnect a signal handler from inside itself

This post explains and shows a code snippet about how to disconnect a signal handler from inside itself, in rust.

All of the rust types used here that are not prefixed by modules names, are in the std library.

In C, what you would do is pass a pointer to malloc-ed heap memory to the user_data argument, that will contain (at some point) the signal handler id, in rust is pretty much the same idea, but it’s a little bit tricky.

You need to first declare a variable with type Rc<RefCell<Option<glib::SignalHandlerId>>>, It’s a Rc “smart pointer”, that points to heap memory allocated RefCell<...>, this RefCell is needed to help us to bypass the static borrow rules, since we can only pass shared references inside of the signal handler, and finally the Option<glib::SignalHandlerId> is a nullable type, since glib::SignalHandlerId doesn’t have a default/zero value, that’s why is needed.

We connect to the signal and pass a cloned Rc, then inside of the signal handler we call disconnect from it when we need to do it.

In code, that would look like this:

let widget: gtk::Widget = ...;

let signal_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Default::default();

// The borrow_mut method comes from RefCell type
*signal_id.borrow_mut() = Some(
    widget.connect_closure(
        "foo",
        false, // after flag
        glib::closure!(
            #[strong]
            signal_id,
            |widget: gtk::Widget| {
                // Some work/condition..., then we disconnect the handler

                if let Some(id) = signal_id.take() {
                    widget.disconnect(id);
                }
            }
        )
    )
);

I hope you find it useful.

This works too (at least, in C):

void super_callback_func(GtkWidget *widget, gpointer userdata)
{
	g_signal_handlers_disconnect_by_func(widget, super_callback_func, userdata);
}

Apart from using signal handler id, it is possible to disconnect (as well as block/unblock) a signal handler by matching:

If a signal handler wants to disconnect itself it has all the necessary data to match itself, nothing must be saved and obtained extra. The signal handler id is needed only if semantics requires it, e.g.: a callback function with identical user data has been connected twice but only one instance must be disconnected.

Those functions are not available in rust language binding.

That’s why I specified the “id” approach, because is the only that works.