actix-derive
actix-derive copied to clipboard
More advanced macros
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)
}
}
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))
}
}
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.
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.
90% use case coverage for new macro should be enough, but this 90% should really useful. other 10% could use normal handler definition.
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.