[][src]Attribute Macro act_zero_macro::act_zero

#[act_zero]

The #[act_zero] macro.

This macro is used in two locations, which will be documented separately:

Trait position

#[act_zero]
trait Greeter {
    fn greet(&self, name: String, res: Sender<String>);
}

This is used to define an actor trait. Actor traits (much like normal traits) define a set of methods to be implemented by the actor type. However, there are a number of constraints:

  • Methods must take &self as the receiver.
  • All argument types must be Send + 'static.
  • Generic methods must have a where Self: Sized bound.
  • There must be no return type.
  • If the last argument is named res, it must be of type Sender<_>.

These last two constraints are related: actor systems work by passing messages and processing them asynchronously. Any return value would not be immediately available, so instead the caller passes in a Sender<_> to receive the result.

As a convenience, there will be an asynchronous call_xyz() method to hide this complexity from the caller:

let addr: Addr<dyn Greeter> = ...;
let res = addr.call_greet("John".into()).await?;

Internally, this macro defines several additional items:

  • trait GreeterImpl You will never use this trait directly, but if you want your trait to be implementable by downstream crates, you must export this trait under the same path as the actor trait itself.
  • enum GreeterMsg This enum contains a variant for each object-safe method in the trait. It is used in remoting scenarios when messages may need to be trasferred between processes or over the network, but is otherwise not used.
  • trait GreeterExt This trait is implemented for the Addr and WeakAddr types when they contain a type implementing the actor trait. Methods on this trait are the primary way you would interact with the actor, and this is where helper methods like call_greeter are defined.

Trait implementation position

#[act_zero]
impl Greeter for SimpleGreeter {
    async fn greet(&mut self, name: String, res: Sender<String>) {
        self.number_of_greets += 1;
        res.send(format!(
            "Hello, {}. You are number {}!",
            name, self.number_of_greets
        ))
        .ok();
    }
}

This is used to implement an actor trait for an actor. The signature of the methods in the implementation differ substantially from those signature in the trait definition:

  • All methods must be async. This may be optional in future.

  • Methods can return any type implementing IntoResult. If an error variant is returned it will be passed to the appropriate error handler implemented for the actor.

  • The receiver type can be any of:

    1. &mut self The method will run to completion with exclusive access to the actor state. No methods taking &self or &mut self can run concurrently. Long running methods taking &mut self should be avoided if you want your actor to stay responsive to new messages.

    2. &self The method will run to completion with shared access to the actor state. Other methods taking &self can run concurrently, but methods taking &mut self cannot.

    3. self: Addr<Local<Self>> The method has no direct access to the actor state, but can run concurrently with any other methods, including those taking &mut self. This can be useful for implementing delayed or timed events without blocking the actor. Access to the actor state can be achieved by awaiting another method on the actor to do the update.

No additional items are generated by the expansion of this variant of the macro.