Socket Programming in Rust

2022-04-02

tl;dr

Ported the code in Beej's Guide to Network Programming to Rust. Find it here.

Unix sockets API sparks joy for me, typing hello in one terminal window and seeing it magically appear in another is a marvelous feeling. Yes it is not as useful as it used to be, yes for real world™ scenarios you would probably be better served by a layer of abstraction over it, but I wanted to experience the joy of reading Beej's Guide to Network Programming once again so I ported all the socket programming related code in it to Rust.

This article is about my experience porting that code and contains some pointers regarding dealing with Unix APIs in Rust.

  1. nix is amazing, it provides friendly bindings to *nix APIs and makes unix systems programming more enjoyable. Often the types used provide additional insight into the API being wrapped over and make it harder to mess things up. More importantly the abstraction layer provided over the *nix APIs is thin and you don't lose any control in the process.

Tip

Read more about the benefits of using nix here.

  1. You can also sidestep nix and directly use libc. I only did that for libc::_exit, but it is nice to have the option of peeling the abstraction layer away.

  2. The Rust compiler ensures safety wherever it can and clearly demarcates the code whose safety is programmer's burden. For example the compiler cannot ensure that all the functions I call inside a signal handler are async-signal-safe so that responsibility is offloaded to me. This concept of clearly marking the code I need to spend more brain cycles on is something I adore. Haskell does that by providing clear indication in the function signature regarding side effects, Rust does that by using unsafe.

If You Get Hit, It's Your Own Fault

  1. There are some C snippets that are seemingly perfect and have survived decades of use without any evolution, rewriting them in Rust can sometimes be a bit tricky. For example this one regarding reaping zombie processes using a SIGCHILD handler:
void sigchld_handler(int s)
{
    // waitpid() might overwrite errno, so we save and restore it:
    int saved_errno = errno;

    while(waitpid(-1, NULL, WNOHANG) > 0);

    errno = saved_errno;
}

Tip

Refer to this article for the nitty-gritty of process termination.

All in all I had a lot of fun with socket programming in Rust, I hope you do too. You can refer to my code here.

This work is licensed under CC BY-NC-SA 4.0.