Your First Actor
You can create a new stewart actor behavior by implementing the Actor
trait.
struct MyActor {
message: String,
}
impl Actor for MyActor {
fn process(&mut self, _world: &mut World, _meta: &mut Metadata) -> Result<(), Error> {
println!("Woken up!");
println!("Message: {}", self.message);
Ok(())
}
}
Actors in stewart are "suspended" using "cooperative multitasking".
When your actor is woken up, its process
callback is called.
Your actor processing doesn't necessarily mean there are messages available to be processed. The only guarantee is that after a "signal" is sent to your actor, your actor will be processed when the world gets to it. If the entire system doesn't get dropped before that, of course.
Adding an actor to a world
Creating an instance of your actor works the same as any other Rust type. A stewart world is simply a collection of actor instances.
To keep your concrete actor type private, you can create a function to start a new instance of your actor.
fn start_my_actor(world: &mut World) -> Result<Signal, Error> {
// Create and insert the actor
let actor = MyActor {};
let id = world.insert("my-actor", actor)?;
// Return the signal to wake it up
let signal = world.signal(id);
Ok(signal)
}
This function returns a Signal
to wake up the actor to keep things simple, but typically you would
return for example a Sender
instead.
The message
module that implements Sender
internally uses Signal
.
You can send a Signal
from anywhere on the same thread, it does not have to happen during actor
processing.
Creating and processing a world
To put it all together, you need to have a world to add your actor to.
After signalling your actor, it will then be processed when calling process
on the world.
fn main() -> Result<(), Error> {
// Create a world with an actor
let mut world = World::default();
let signal = start_my_actor(&mut world)?;
// Wake up the actor
signal.send();
world.process()?;
Ok(())
}
This is the most basic, and perfectly functional, way to create an actor runtime.
If all your actors block internally and never have to wait on external data this works perfectly
fine as process
will keep running until no actors are waiting for processing.
If you do however have things your world may need to wait on while not processing an actor, you
can implement this here.
For example, stewart-mio
implements a world processing loop based on a mio event loop.