My name is Tage Johansson, and I have just completed my project for Google Summer of Code the summer 2023. This is a short description of what I've done and some thoughts about the project.
Network Block Device (NBD) is a protocol for exporting block devices, either between processes or across the network. Libnbd is a client library for talking to NBD servers. This library had bindings in various languages, including C, Golang and Python, but not in Rust. During the summer in 2023, I participated in Google Summer of Code (GSoC), and the goal of my project was to make ergonomic Rust bindings for Libnbd.
Libnbd aims to provide bindings for as many languages as possible. In order not to have to update the bindings for each language individually, the API is defined in some OCaml files, and the bindings are then automatically generated from these bindings by some other OCaml files. So when the API is updated, it will be reflected in all language bindings with little to no intervention by the programmer.
As my goal for this project was to add bindings for Rust, I had to create some OCaml files, (Rust.ml and RustSys.ml), which generates the necessary Rust files. The generated Rust code make calls to a shared object file (libnbd.so) via Rust's foreign function interface (FFI).
A top level directory "rust" was created, containing the actual Rust crate for Libnbd. Apart from the automatically generated Rust files, this directory also contains several static Rust files defining things like general error types and helper functions. When building, after the OCaml generator is complete, this directory will be compiled as a Rust crate by Rust's build tool Cargo.
As Libnbd API allows for multiple concurrent calls to the same server, I wanted to investigate whether it would be possible to create an asynchronous version of the bindings, making use of Rust's asynchronous programming tools. The result was another 500 lines of code (plus tests and examples), half of which were OCaml and half of which were Rust. Under the hood, the asynchronous bindings uses Tokio (the at the time of writing most popular asynchronous runtime for Rust), for keeping track of updates to the file descriptor. The asynchronous bindings turned out to be fairly easy to use, and I am quite proud of them. Although most credit goes to Rust's excellent support for asynchronous programming.
The code for the normal (not asynchronous) Rust bindings is complete and merged. And the code for the asynchronous Rust bindings is also nearly complete but is still out for review. Apart from that, some more documentation describing how to use the bindings has yet to be written. And last but not least, the libnbd crate should hopefully be published to crates.io.
I have a great passion for programming languages, so this has been a really fun project as I have been dealing with three different languages: OCaml for writing the bindings-generator. Rust because that's the target language of the bindings. And the Rust code is communicating with the C library so I had to read and understand a lot of C code as well.
It was also the first time I made a significant contribution to an open source project. Something I really enjoyed and would like to do more in the future.
All in all, I think Google Summer of Code is an excellent opportunity for a summer internship, and I would encourage other students to try it out.
I have contributed a delta-count of 3043 lines divided onto 13 commits which are merged upstream so far. In case someone would be interested, I have compiled a list of links to all commits I have contributed below:
- 557ed93 -- generator: Move [camel_case] function from GoLang.ml to utils.ml
- 10b37a2 -- generator: Add copyright argument to
generate_header
- 3f5b9d2 -- generator: Add optional output_to ?formatter parameter
- 1c5d38e -- generator: Add .ocamlformat file
- b2511d6 -- rust: create basic Rust bindings
- 785d5f5 -- rust: Add a couple of integration tests
- ae46b96 -- rust: Add some examples
- b656d1d -- rust: Make it possible to run tests with Valgrind
- 41e921b -- rust: Add cargo as an optional build dependency in README
- ef62e94 -- rust: Specify minimum Rust version in Cargo.toml
- 086999e -- rust: Correct license field in Cargo.toml
- 3ae753c -- rust: Format some files to conform to rustfmt and rustfmt.toml
- 49a97e3 -- rust: Make it possible to run examples with a URI
See this nice gist for some git commands to retrieve the above statistics.