| 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!