pub trait NewActor {
    type Message;
    type Argument;
    type Actor: Actor;
    type Error;
    type RuntimeAccess;

    fn new(
        &mut self,
        ctx: Context<Self::Message, Self::RuntimeAccess>,
        arg: Self::Argument
    ) -> Result<Self::Actor, Self::Error>; fn map_arg<F, Arg>(self, f: F) -> ArgMap<Self, F, Arg>
    where
        Self: Sized,
        F: FnMut(Arg) -> Self::Argument
, { ... } fn name() -> &'static str { ... } }
Expand description

The trait that defines how to create a new Actor.

The easiest way to implement this by using an asynchronous function, see the actor module documentation.

Required Associated Types

The type of messages the actor can receive.

Using an enum allows an actor to handle multiple types of messages.

Examples

Here is an example of using an enum as message type.

#![feature(never_type)]

use heph::supervisor::NoSupervisor;
use heph::{actor, from_message};
use heph_rt::spawn::ActorOptions;
use heph_rt::{self as rt, Runtime, ThreadLocal};

fn main() -> Result<(), rt::Error> {
    // Create and run the runtime.
    let mut runtime = Runtime::new()?;
    runtime.run_on_workers(|mut runtime_ref| -> Result<(), !> {
        // Spawn the actor.
        let new_actor = actor as fn(_) -> _;
        let actor_ref = runtime_ref.spawn_local(NoSupervisor, new_actor, (), ActorOptions::default());

        // Now we can use the reference to send the actor a message. We
        // don't have to use `Message` type we can just use `String`,
        // because `Message` implements `From<String>`.
        actor_ref.try_send("Hello world".to_owned()).unwrap();
        Ok(())
    })?;
    runtime.start()
}

/// The message type for the actor.
#[derive(Debug)]
enum Message {
    String(String),
    Number(usize),
}

// Implementing `From` for the message allows us to just pass a
// `String`, rather then a `Message::String`. See sending of the
// message in the `setup` function.
from_message!(Message::String(String));
from_message!(Message::Number(usize));

/// Our actor implementation that prints all messages it receives.
async fn actor(mut ctx: actor::Context<Message, ThreadLocal>) {
    if let Ok(msg) = ctx.receive_next().await {
        println!("received message: {:?}", msg);
    }
}

The argument(s) passed to the actor.

The arguments passed to the actor are much like arguments passed to a regular function. If more then one argument is needed the arguments can be in the form of a tuple, e.g. (123, "Hello"). For example TcpServer requires a NewActor where the argument is a tuple (TcpStream, SocketAddr).

An empty tuple can be used for actors that don’t accept any arguments (except for the actor::Context, see new below).

When using asynchronous functions arguments are passed regularly, i.e. not in the form of a tuple, however they do have be passed as a tuple to ActorFuture::new. See there implementations below.

The type of the actor.

See Actor for more.

The type of error.

Note that if creating an actor is always successful the never type (!) can be used. Asynchronous functions for example use the never type as error.

The kind of runtime access needed by the actor.

The runtime is accessible via the actor’s context. See actor::Context for more information.

Required Methods

Create a new Actor.

Provided Methods

Wrap the NewActor to change the arguments its accepts.

This can be used when additional arguments are needed to be passed to an actor, where another function requires a certain argument list. For example when using TcpServer.

Examples

Using TcpServer requires a NewActor that accepts (TcpStream, SocketAddr) as arguments, but we need to pass the actor additional arguments.

#![feature(never_type)]

use std::io;
use std::net::SocketAddr;

use heph::actor::{self, NewActor};
use heph_rt::net::{TcpServer, TcpStream};
use heph_rt::spawn::ActorOptions;
use heph_rt::{self as rt, Runtime, RuntimeRef, ThreadLocal};

fn main() -> Result<(), rt::Error> {
    // Create and run runtime
    let mut runtime = Runtime::new()?;
    runtime.run_on_workers(setup)?;
    runtime.start()
}

/// In this setup function we'll spawn the `TcpServer` actor.
fn setup(mut runtime_ref: RuntimeRef) -> io::Result<()> {
    // Prepare for humans' expand to Mars.
    let greet_mars = true;

    // Our actor that accepts three arguments.
    let new_actor = (conn_actor as fn(_, _, _, _) -> _)
        .map_arg(move |(stream, address)| (stream, address, greet_mars));

    // For more information about the remainder of this example see
    // `TcpServer`.
    let address = "127.0.0.1:7890".parse().unwrap();
    let server = TcpServer::setup(address, conn_supervisor, new_actor, ActorOptions::default())?;
    runtime_ref.try_spawn_local(ServerSupervisor, server, (), ActorOptions::default())?;
    Ok(())
}

// Actor that handles a connection.
async fn conn_actor(
    _: actor::Context<!, ThreadLocal>,
    mut stream: TcpStream,
    address: SocketAddr,
    greet_mars: bool
) -> io::Result<()> {
    if greet_mars {
        // In case this example ever reaches Mars.
        stream.send_all(b"Hello Mars").await
    } else {
        stream.send_all(b"Hello World").await
    }
}

Returns the name of the actor.

The default implementation creates the name based on the type name of the actor.

Notes

This uses type_name under the hood which does not have a stable output. Like the type_name function the default implementation is provided on a best effort basis.

Implementations on Foreign Types

Implementors