spaad icon indicating copy to clipboard operation
spaad copied to clipboard

Support For Custom Context With Multiple Actor Instances

Open zicklag opened this issue 3 years ago • 5 comments

Hey there, I just discovered this along with xtra and while trying to decide whether or not to use spaad I needed to find a way to spawn actors with the message-stealing strategy, but I couldn't find a way to do it with the current version of spaad. It would be great if we could find a way to do that with spaad.

Good job on both libraries, by-the-way. I like the minimal, but still rather functional design, and using spaad creates a rather interesting scenario with the actors almost abstracted out of sight. I'm still deciding whether I like invisible actors or not, but I think I've got to try using it a little bit to make up my mind. :)

zicklag avatar Feb 12 '22 03:02 zicklag

Hi, do you mean having multiple actors on one address, just to check?

Restioson avatar Feb 12 '22 07:02 Restioson

Yeah. Essentially a way to do what you have in the example here:

let (addr, mut ctx) = Context::new(Some(32));
for n in 0..3 {
    smol::spawn(ctx.attach(MyActor::new(n))).detach();
}
ctx.run(MyActor::new(4)).await;

So that multiple actor instances can handle messages sent to the one addr.

After thinking about it, though, I think I might prefer to to just use xtra directly, so don't bother working on this feature just for me. :slightly_smiling_face:

I'm going to be doing some experimentation with it and I might end up trying to implement it if I find it useful, but I'll have to do some more testing first.

zicklag avatar Feb 12 '22 18:02 zicklag

Anyway, this is what I settled on using for now, which is almost straight up xtra with just a small macro for implementing the handlers. I figured I'd share here, just in case the perspective helps to share a user's ( my ) use-case and preference ( so far ):

/// Macro to easily add handlers handlers for [`xtra`] actors.
/// 
/// ## Usage Example
/// 
/// ```
/// pub struct Listener;
/// impl Actor for Listener {}
/// 
/// pub struct Hello;
/// impl Message for Hello {
///     type Result = 
/// }
/// 
/// pub struct Goodbye;
/// impl Message for Goodbye {
///     type Result = ();
/// }
/// 
/// crate::actor_handlers!(Listener, {
///     async fn handle_hello(&mut self, _: Hello, _ctx: &mut Context<Self>) {
///         println!("Hello! 👋")
///     }
/// 
///     async fn handle_stop(&mut self, _: Goodbye, _ctx: &mut Context<Self>) {
///         println!("Goodbye. 😔");
///     }
/// });
/// ```
#[macro_export(crate)]
macro_rules! actor_handlers {
    (
        $actor:ty,
        {
            $(
                $(#[$attrs:meta])*
                async fn $handler:ident(&mut self, $msg:tt: $message_type:path, $ctx:ident: &mut Context<Self>) $(-> $result:path )? {
                    $($bodyTokens:tt)*
                }
            )*
        }
        $(,)?
    ) => {
        impl $actor {
            $(
                async fn $handler(&mut self, $msg: $message_type, $ctx: &mut xtra::Context<Self>) $(-> $result )? {
                    $($bodyTokens)*
                }
            )*
        }

        $(
            #[async_trait::async_trait]
            impl xtra::Handler<$message_type> for $actor {
                async fn handle(&mut self, msg: $message_type, ctx: &mut xtra::Context<Self>) $(-> $result )? {
                    self.$handler(msg, ctx).await
                }
            }
        )*
    };
}

The biggest motivation for me to not just use xtra plain is the problem with IDE completion when using using the #[async_trait] macro. When you put many ( but maybe not all ) attribute macros on an impl block, while I'm typing and I hit the period, the IDE will, instead of providing auto-complete options, just report a syntax error from the macro, because the macro just aborts processing because of my invalid syntax. it essentially kills all of Rust analyzer's auto-complete while implementing the function body.

Using spaad solved that issue for me, probably due to the way you implemented the macro. I should probably just open an issue on either Rust analyzer or async_trait, but I hadn't wanted to spend the time to investigate the issue yet, so I just used the workaround above, and I think I don't dislike it horribly yet. :)

Anyway, that's all a little off-topic, but it's not really an actionable item so I didn't know if it warranted another issue.

zicklag avatar Feb 12 '22 22:02 zicklag

Hmm, that Rust analyzer issue is really strange! I'm using IntelliJ Rust so this hasn't been an issue.

The main issue with exposing this functionality would be finding a nice way to do it on the spaad side! I'm not exactly sure what kind of interface would be best here. However, if you've got a different solution, then maybe it's not very important. Could be nice to have later though.

Restioson avatar Feb 13 '22 20:02 Restioson

Hmm, that Rust analyzer issue is really strange! I'm using IntelliJ Rust so this hasn't been an issue.

I just tested updating rust-analyzer to the latest version and it looks like it's fixed! Yay! :tada: That helps my situation a lot.

zicklag avatar Feb 14 '22 22:02 zicklag