Skip to main content

black_box/
mailbox.rs

1use std::{future::Future, pin::Pin, sync::Arc};
2
3use crate::{
4    actors::{Actor, Address, Handler},
5    message::Message,
6};
7
8trait MailboxSender<T: Message>: Send + Sync {
9    fn send<'a>(&'a self, msg: T) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
10    fn try_send(&self, msg: T);
11}
12
13impl<A, T> MailboxSender<T> for Address<A>
14where
15    A: 'static + Actor + Send + Handler<T>,
16    T: Message,
17{
18    fn send<'a>(&'a self, msg: T) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
19        Box::pin(Address::send(self, msg))
20    }
21
22    fn try_send(&self, msg: T) {
23        Address::try_send(self, msg);
24    }
25}
26
27/// A type-erased, cloneable handle for sending messages of type `T` to an actor.
28///
29/// Unlike [`Address<A>`], `Mailbox<T>` is generic over the message type rather than the actor
30/// type. This allows passing a mailbox to code that only needs to send a specific message type
31/// without exposing the concrete actor `A`.
32///
33/// Construct a `Mailbox<T>` from an [`Address<A>`] via [`Address::into_mailbox`].
34pub struct Mailbox<T: Message> {
35    sender: Arc<dyn MailboxSender<T>>,
36}
37
38impl<T: Message> Clone for Mailbox<T> {
39    fn clone(&self) -> Self {
40        Self {
41            sender: Arc::clone(&self.sender),
42        }
43    }
44}
45
46impl<T: Message> std::fmt::Debug for Mailbox<T> {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        f.debug_struct("Mailbox").finish_non_exhaustive()
49    }
50}
51
52impl<T: Message> Mailbox<T> {
53    /// Send a message, awaiting if the channel is full.
54    pub async fn send(&self, msg: T) {
55        self.sender.send(msg).await
56    }
57
58    /// Try to send a message without awaiting. The message is dropped if the channel is full.
59    pub fn try_send(&self, msg: T) {
60        self.sender.try_send(msg);
61    }
62}
63
64impl<A> Address<A>
65where
66    A: 'static + Actor + Send,
67{
68    /// Convert this address into a [`Mailbox<T>`], erasing the actor type.
69    ///
70    /// The resulting mailbox can send messages of type `T` to the actor without exposing `A`.
71    pub fn into_mailbox<T>(self) -> Mailbox<T>
72    where
73        A: Handler<T>,
74        T: Message,
75    {
76        Mailbox {
77            sender: Arc::new(self),
78        }
79    }
80}
81
82impl<A, T> From<Address<A>> for Mailbox<T>
83where
84    A: 'static + Actor + Send + Handler<T>,
85    T: Message,
86{
87    fn from(address: Address<A>) -> Self {
88        address.into_mailbox()
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use crate::{executor::Context, Executor};
95
96    use super::*;
97
98    struct Ping;
99    struct Pong;
100
101    struct MyActor;
102    impl Actor for MyActor {}
103
104    impl Handler<Ping> for MyActor {
105        async fn handle(&mut self, _msg: Ping, _ctx: &Context<Self>) {}
106    }
107
108    impl Handler<Pong> for MyActor {
109        async fn handle(&mut self, _msg: Pong, _ctx: &Context<Self>) {}
110    }
111
112    #[test]
113    fn into_mailbox_compiles() {
114        let (_executor, address) = Executor::new(MyActor);
115        let _mailbox: Mailbox<Ping> = address.into_mailbox();
116    }
117
118    #[test]
119    fn from_address_compiles() {
120        let (_executor, address) = Executor::new(MyActor);
121        let _mailbox: Mailbox<Pong> = Mailbox::from(address);
122    }
123
124    #[test]
125    fn mailbox_is_clone() {
126        let (_executor, address) = Executor::new(MyActor);
127        let mailbox: Mailbox<Ping> = address.into_mailbox();
128        let _ = mailbox.clone();
129    }
130
131    #[test]
132    fn mailbox_is_debug() {
133        let (_executor, address) = Executor::new(MyActor);
134        let mailbox: Mailbox<Ping> = address.into_mailbox();
135        let _ = format!("{:?}", mailbox);
136    }
137}