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 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.
type RuntimeAccess
type RuntimeAccess
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
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
}
}