actix-derive icon indicating copy to clipboard operation
actix-derive copied to clipboard

More advanced macros

Open callym opened this issue 7 years ago • 5 comments

I implemented a #[handler] macro to allow you to tag functions as a handler for a type, and generate the necessary code implementing the Handler trait for the type. Is this useful?

It'd require Nightly because it depends on feature(proc_macro), but that can be feature-gated, so the custom derives would still work on Stable.

#[handler]
impl SumActor {
    #[handle(Sum)]
    fn sum(&mut self, message: Sum, _: &mut Context<Self>) -> Response<Self, Sum> {
        println!("{}", message.0 + message.1);
        Self::reply(message.0 + message.1)
    }
}

Which would expand to:

impl SumActor {
    fn sum(&mut self, message: Sum, _: &mut Context<Self>) -> Response<Self, Sum> {
        println!("{}", message.0 + message.1);
        Self::reply(message.0 + message.1)
    }
}

impl Handler<Sum> for SumActor {
    fn handle(&mut self, message: Sum, context: &mut Context<Self>) -> Response<Self, Sum> {
        self.sum(message, context)
    }
}

callym avatar Nov 16 '17 17:11 callym

i see handler something like:

#[derive(Message)]
#[response(usize)]
struct Sum {
  a: usize,
  b: usize,
}

#[handler]
impl SumActor {
    #[handle(Sum)]
    fn sum(&mut self, a: usize, b: usize) -> usize {
        println!("{}", a + b);
        a + b
    }
}

expands to:

impl SumActor {
    fn sum(&mut self, a: usize, b: usize) -> usize {
        println!("{}", a + b);
        a + b
    }
}

impl Handler<Sum> for SumActor {
    fn handle(&mut self, message: Sum, context: &mut Context<Self>) -> Response<Self, Sum> {
        Self::reply(self.sum(message.a, message.b))
    }
}

fafhrd91 avatar Nov 16 '17 18:11 fafhrd91

In your example, how would you go from message: Sum to a: usize, b: usize in more complex cases, such as Structs, where you can't simply unwrap the type.

What about:

#[handler]
impl VectorActor {
    #[handle(Sum)]
    fn magnitude(&mut self, message: Sum) -> usize {
        sum.0 + sum.1
    }
}

With the expansion being:

impl Handler<Sum> for SumActor {
    fn handle(&mut self, message: Sum, context: &mut Context<Self>) -> Response<Self, Sum> {
        Self::reply(self.sum(message))
    }
}

For messages with () as a reply type, it can simply be:

#[handle(Message)]
fn reply(&mut self, message: Message) {
    // do work here
}

This still hides the context: &mut Context<Self> and Response<Self, ...> from the signature with slightly less magic involved.

callym avatar Nov 22 '17 19:11 callym

I do not think tuple struct should be supported. and with normal struct we can destructor it by names, which we can take from handle definition. ctx could be optional param.

there is no point to introduce new macro just to reduce code by couple lines. and point of macro should be ergonomic.

fafhrd91 avatar Nov 23 '17 21:11 fafhrd91

90% use case coverage for new macro should be enough, but this 90% should really useful. other 10% could use normal handler definition.

fafhrd91 avatar Nov 23 '17 21:11 fafhrd91

I've been thinking about this, and have something that works like

#[handler]
impl SomeActor {
    #[handle]
    fn add(&self, sum: Sum) -> usize {
        sum.0 + sum.1
    }

    #[handle]
    fn empty(&mut self, _: Empty, ctx: &mut Context<Self>) {
        // do whatever here
    }
}

The message type is taken from the handle fn arguments, return type is wrapped in Self::reply inside the macro, and ctx is optional. Both &self and &mut self work too.

callym avatar Nov 30 '17 12:11 callym