Rate this Page
#[derive(HandleClient)]">

#[derive(HandleClient)]#

#[derive(Handler)] generates both the server-side handler trait (ShoppingListHandler) and the client-side trait definition (ShoppingListClient). However, it does not implement the client trait for any specific type.

This is where #[derive(HandleClient)] comes in.

What It Adds#

#[derive(HandleClient)] generates the following implementation:

impl<T> ShoppingListClient for ActorHandle<T>
where
  T: ShoppingListHandler + Send + Sync + 'static`

This means you can call methods like .add(...) or .list(...) directly on an ActorHandle<T> without needing to manually implement the ShoppingListClient trait:

In other words, HandleClient connects the generated ShoppingListClient interface (from Handler) to the concrete type ActorHandle<T>.

Generated Implementation (simplified)#

use async_trait::async_trait;
use hyperactor::{
    ActorHandle,
    anyhow::Error,
    cap::{CanSend, CanOpenPort},
    mailbox::open_once_port,
    metrics,
    Message,
};

#[async_trait]
impl<T> ShoppingListClient for ActorHandle<T>
where
    T: ShoppingListHandler + Send + Sync + 'static,
{
    async fn add(&self, caps: &impl CanSend, item: String) -> Result<(), Error> {
        self.send(caps, ShoppingList::Add(item)).await
    }

    async fn remove(&self, caps: &impl CanSend, item: String) -> Result<(), Error> {
        self.send(caps, ShoppingList::Remove(item)).await
    }

    async fn exists(
        &self,
        caps: &impl CanSend + CanOpenPort,
        item: String,
    ) -> Result<bool, Error> {
        let (reply_to, recv) = open_once_port(caps)?;
        self.send(caps, ShoppingList::Exists(item, reply_to)).await?;
        Ok(recv.await?)
    }

    async fn list(
        &self,
        caps: &impl CanSend + CanOpenPort,
    ) -> Result<Vec<String>, Error> {
        let (reply_to, recv) = open_once_port(caps)?;
        self.send(caps, ShoppingList::List(reply_to)).await?;
        Ok(recv.await?)
    }