chatty-catties/nebkor-maelstrom/README.md
2024-06-04 10:21:36 -07:00

2.2 KiB

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!

TODO

  • add error handling.