#469·actix

A macro to generate message handler boilerplate

Author: piegamesdeCreated Feb 27, 2021Updated Dec 14, 2024

When adding messages to an actor, I noticed that it very often follows some specific pattern, that involves a bit of boilerplate code:

  • Create the method that does something on the impl MyActor
  • Create a struct that represents the arguments of that method and derive actix::Message for it
  • Implement actix::Handler<SomeMessage> for MyActor by having the handle method delegate to the associated method

I have the idea of reducing the boilerplate (and the number of structs) involved by having a proc-macro generate most of it. A first step towards this, which only automates the impl Handler, could look like this:

rust
impl MyActor {
    /* Simple case */
    #[handler(SomeMessage)]
    fn update_content(&mut self, event: SomeMessage, ctx: &mut Self::Context) { todo!() }

    /* The macro needs to parse the return type from the method for the trait implementation */
    #[handler(SomeMessage)]
    fn update_content(&mut self, event: SomeMessage, ctx: &mut Self::Context) -> SomeResponse { todo!() }

    /* Optional: if the function is `async`, automatically wrap `<MyActor as Handler<SomeMessage>>::Result` in a future */
    #[handler(SomeMessage)]
    async fn update_content(&mut self, event: SomeMessage, ctx: &mut Self::Context) -> SomeResponse { todo!() }
}

A second step then would be to somehow extract the function arguments into their own struct. I am sorry that I cannot prototype this myself, as I don't have enough proc macro skills.


Motivation: I am working on a GTK application using woab. GTK uses a lot of signals and handlers, resulting in a lot of boilerplate code. Therefore, woab currently exposes all signals as variants of one message enum. But I think that having a proper message per signal type would be more idiomatic.