9 releases
0.2.0 | Dec 28, 2020 |
---|---|
0.1.1 | Jan 8, 2020 |
0.1.0 | Nov 8, 2019 |
0.0.6 | Aug 20, 2019 |
0.0.4 | Feb 25, 2018 |
#1439 in Asynchronous
13KB
106 lines
lwactors
Lightweight actors for Rust using futures-rs
.
Introduction
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.
Overview
The actor itself
An application would contain a bespoke struct for each actor, which would own a reference to the underlying future:
#[derive(Clone)]
struct Counter {
queue: ActorSender<CounterAction, i64, CounterError>,
}
When constructed lwactors
actor
function is called to start the actor running:
fn new(cpu_pool: &CpuPool) -> Counter {
Counter {
queue: actor(cpu_pool, 0),
}
}
it requires a reference to a CpuPool
(or another Executor
) and an initial state, in this case 0
.
Action - the set of possible messages
The application will then define the set of messages that can be sent to the actor:
#[derive(Debug)]
enum CounterAction {
Add(i64),
Subtract(i64),
}
and the code that the actor will run when processing the message:
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);
}
}
Usage
Finally, to expose a friendly high-level API, the application can implement specific functions which are responsible for sending messages to the actor:
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.
Dependencies
~0.8–15MB
~146K SLoC