examples | ||
src | ||
.gitignore | ||
.rustfmt.toml | ||
Cargo.toml | ||
LICENSE.md | ||
README.md |
A synchronous and simple Maelstrom crate
nebkor-maelstreom
is a lean and simple synchronous library for writing
Maelstrom-compatible
distributed actors. It has three dependencies:
- serde
- serde_json
- serde_repr
For a simple example, see the gg-echo program:
use nebkor_maelstrom::{Body, Message, Node, Runner};
struct Echo;
impl Node for Echo {
fn handle(&mut self, runner: &Runner, msg: Message) {
let typ = &msg.body.typ;
if typ.as_str() == "echo" {
let body = Body::from_type("echo_ok").with_payload(msg.body.payload.clone());
runner.reply(&msg, body);
}
}
}
fn main() {
let node = Echo;
let runner = Runner::new(node);
runner.run(None);
}
How to use
Create a struct and implement nebkor_maelstrom::Node
for it, which involves a single method,
handle(&mut self, &Runner, Message)
. This method is passed a Runner
which contains methods like
send
, reply
, and rpc
.
In your main function, instantiate that struct and pass that into Runner::new()
to get a
Runner. The run()
method takes an optional closure that will be run when the init
Message is
received; see the
broadcast
program for an example of that, where it spawns a thread to send periodic messages to the node.
Design considerations
I wanted the client code to be as simple as possible, with the least amount of boilerplate. Using
&mut self
as the receiver for the handle()
method lets you easily mutate state in your node if
you need to, without the ceremony of Rc<Mutex<>>
and the like. Eschewing async
results in an
order of magnitude fewer dependencies, and the entire workspace (crate and clients) can be compiled
in a couple seconds.
Acknowledgments
I straight-up stole the design of the IO/network system from Maelbreaker, which allowed me to get a working RPC call. Thanks!