You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
3.2 KiB
98 lines
3.2 KiB
1 year ago
|
+++
|
||
|
title = "Thinking About Rust Actors"
|
||
|
date = 2023-08-11
|
||
|
draft =true
|
||
|
|
||
|
[taxonomies]
|
||
|
tags = ["rust", "actor model"]
|
||
|
+++
|
||
|
|
||
|
I recently wrote an application for work (so, sorry, can't show you the code)
|
||
|
that, 'cause it was heavily I/O based, I decided to write it using
|
||
|
[Tokio](https://tokio.rs/) and the idea of [Actor Model with
|
||
|
it](https://ryhl.io/blog/actors-with-tokio/).
|
||
|
|
||
|
... which gave me some things to think about.
|
||
|
|
||
|
<!-- more -->
|
||
|
|
||
|
Before anything, actors in Rust are very different from the actors in languages
|
||
|
with the actual Actor Model. In summary, you have your actors, which running
|
||
|
independently, each actor have an Inbox for things to be processed and an
|
||
|
"outbox" -- in quotes, 'cause that's not really it. An actor can receive a
|
||
|
message, process it and then it can just be done with it or it can produce
|
||
|
something that it is send to another actor -- that's its outbox, which usually
|
||
|
differs from the Inbox 'cause the Inbox need to have a queue of sorts, but the
|
||
|
Outbox doesn't (and that's why I've been using "outbox" with quotes before).
|
||
|
|
||
|
All the messages are delivered by a "post office" of sorts, that connects all
|
||
|
Actors:
|
||
|
|
||
|
![](actors.png "A silly representation of the actor model")
|
||
|
|
||
|
On my implementation, the actor is actually a module with a `run()` function;
|
||
|
this function exposes the `Sender` part of a channel which acts as the Inbox of
|
||
|
it and the task PID, so the can `.await` it to avoid the main application from
|
||
|
finishing with the actor still running.
|
||
|
|
||
|
And because there is no "Post Office" kind of solver in Rust, we can actually
|
||
|
short circuit the actors by giving the `Sender` channel of an actor as
|
||
|
parameter to a second, so it knows where to send its messages. Something like:
|
||
|
|
||
|
```rust
|
||
|
let channel3 = actor3::run(...).await;
|
||
|
let channel2 = actor2::run(channel3).await;
|
||
|
actor1::run(channel2).await;
|
||
|
```
|
||
|
|
||
|
In this short sample, whatever "actor1" produces, it sends directly to
|
||
|
"actor2"; "actor2", on its part, produces something that is received by
|
||
|
"actor3". And, with more actors, things just keep chaining.
|
||
|
|
||
|
{% note() %}
|
||
|
I am intentionally ignoring the internals of each actor and their `run()`
|
||
|
function, but they are some variations of:
|
||
|
|
||
|
```rust
|
||
|
fn run(..) -> (task::JoinHandle<()>, mpsc::Sender<TheKindOfMessageTheActorAccepts>) {
|
||
|
let (tx, mut rx) = mpsc::channel::<TheKindOfMessageTheActorAccepts>(SOME_SIZE);
|
||
|
let task = tokio::spawn(async move {
|
||
|
while let Some(incoming) = rx.recv().await {
|
||
|
let conversion = actor_process(incoming);
|
||
|
// maybe send the conversion to the next actor?
|
||
|
}
|
||
|
});
|
||
|
(task, tx)
|
||
|
}
|
||
|
```
|
||
|
{% end %}
|
||
|
|
||
|
But... 'cause the actors have (very similar) interfaces, that looks like a
|
||
|
trait!
|
||
|
|
||
|
So, what should be the Actor trait?
|
||
|
|
||
|
First thing, its `new()` or similar function should expose its PID. Something
|
||
|
like:
|
||
|
|
||
|
```rust
|
||
|
pub trait Actor {
|
||
|
fn new(..) -> Sender<TheKindOfMessageTheActorAccepts>;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Why `TheKindOfMessageTheActorAccepts`? That's because each actor may have a
|
||
|
different input message. If we take our short sample above, "actor2" may be
|
||
|
receiving `usize`s and sending them as `String`s to "actor3".
|
||
|
|
||
|
Because that type may change from actor to actor, it should be an associated
|
||
|
type:
|
||
|
|
||
|
```rust
|
||
|
pub trait Actor {
|
||
|
type Input = TheKindOfMessageTheActorAccepts;
|
||
|
|
||
|
fn new(..) -> Sender<Self::Input>;
|
||
|
}
|
||
|
```
|