@startuml Message Passing
title Message Passing (tell and ask)
actor Client
participant "ActorRef<T>" as ActorRef_obj
participant "MailboxChannel (mpsc)" as MailboxChannel
participant "run_actor_lifecycle()" as Runtime_obj
participant "MessageHandler::handle()" as DynMsgHandler
participant "UserActor::handle(msg)" as UserHandler
participant "oneshot::channel" as OneshotChannel
box "Actor Task" #LightBlue
participant Runtime_obj
participant DynMsgHandler
participant UserHandler
end box
== Tell Operation (Fire-and-Forget) ==
Client -> ActorRef_obj: actor_ref.tell(MyMessage).await
activate ActorRef_obj
ActorRef_obj -> ActorRef_obj: Wraps MyMessage in MailboxMessage::Envelope (reply_channel = None)
ActorRef_obj -> MailboxChannel: sender.send(envelope).await
deactivate ActorRef_obj
Client <-- ActorRef_obj: Returns Result<()>
MailboxChannel -> Runtime_obj: tokio::select! receives Envelope from mailbox
activate Runtime_obj
Runtime_obj -> DynMsgHandler: payload.handle_message(&mut actor, actor_ref, reply_channel).await
activate DynMsgHandler
DynMsgHandler -> UserHandler: Calls specific Message<MyMessage>::handle method
activate UserHandler
UserHandler --> DynMsgHandler: Returns Reply (e.g., ())
deactivate UserHandler
DynMsgHandler --> Runtime_obj: Message processing complete
deactivate DynMsgHandler
note right of Runtime_obj: For tell, reply is discarded (no reply_channel)
Runtime_obj -> Runtime_obj: Continue select! loop
deactivate Runtime_obj
== Ask Operation (Request-Reply) ==
Client -> ActorRef_obj: actor_ref.ask(MyQuery).await
activate ActorRef_obj
ActorRef_obj -> OneshotChannel: Creates (reply_tx, reply_rx)
ActorRef_obj -> ActorRef_obj: Wraps MyQuery in MailboxMessage::Envelope (with reply_tx)
ActorRef_obj -> MailboxChannel: sender.send(envelope).await
ActorRef_obj -> OneshotChannel: reply_rx.await (waits for reply)
activate OneshotChannel
MailboxChannel -> Runtime_obj: tokio::select! receives Envelope from mailbox
activate Runtime_obj
Runtime_obj -> DynMsgHandler: payload.handle_message(&mut actor, actor_ref, Some(reply_channel)).await
activate DynMsgHandler
DynMsgHandler -> UserHandler: Calls specific Message<MyQuery>::handle method
activate UserHandler
UserHandler --> DynMsgHandler: Returns QueryReply
deactivate UserHandler
DynMsgHandler -> OneshotChannel: reply_tx.send(QueryReply) (via reply_channel)
DynMsgHandler --> Runtime_obj: Message processing complete
deactivate DynMsgHandler
OneshotChannel --> ActorRef_obj: Receives reply
deactivate OneshotChannel
ActorRef_obj -> ActorRef_obj: Type-safe reply (no downcasting needed with ActorRef<T>)
ActorRef_obj --> Client: Returns QueryReply
deactivate ActorRef_obj
Runtime_obj -> Runtime_obj: Continue select! loop
deactivate Runtime_obj
note over Client, UserHandler
Type Safety: ActorRef<T> ensures compile-time type checking.
Only messages that actor T implements handlers for can be sent.
Reply types are also statically verified.
end note
@enduml