Lightweight actors for Rust using futures-rs
.
The TL;DR is that you probably want to use Actix instead.
This library allows standalone "actors" to exist in Applications using Futures.
An actor is created with an initial state. From there the state can only be queried or modified by passing messages using the invoke
function, receiving a future which will contain a result once the message has been processed. The messages are processed asynchronously.
See the "counter" example for an suggestion of how to build actors around this library.
An application would contain a bespoke struct for each actor, which would own a reference to the underlying future:
```rust
struct Counter {
queue: ActorSender
When constructed lwactors
actor
function is called to start the actor running:
rust
fn new(cpu_pool: &CpuPool) -> Counter {
Counter {
queue: actor(cpu_pool, 0),
}
}
it requires a reference to a CpuPool
and an initial state, in this case 0
.
The application will then define the set of messages that can be sent to the actor:
```rust
enum CounterAction { Add(i64), Subtract(i64), } ```
and the code that the actor will run when processing the message:
rust
impl Action<i64, i64, CounterError> for CounterAction {
fn act(self, state: &mut i64) -> Result<i64, CounterError> {
println!("Acting {:?} on {}", self, state);
match self {
CounterAction::Add(i) => *state += i,
CounterAction::Subtract(i) => *state -= i,
}
return Ok(*state);
}
}
Finally, to expose a friendly high-level API, the application can implement specific functions which are responsible for sending messages to the actor:
```rust impl Counter { // new function quoted above goes here
fn add(&self, n: i64) -> CounterFuture {
self.queue.invoke(CounterAction::Add(n))
}
fn subtract(&self, n: i64) -> CounterFuture {
self.queue.invoke(CounterAction::Subtract(n))
}
} ```
The struct that contains the reference to the actor Counter
is cheaply cloneable, but all refer to the same running future that processes the messages, so all indirectly refer to the same shared state. Each thread that owns a Counter
can call add
or subtract
to send messages, receiving a future that resolves to an i64
which is the state of the counter after that message has been processed.
The running future that processes messages will complete successfully once all the Counter
s have been dropped.