Amateur Hour

A small shell in rust

Or I've seen the light
Sep 24, 2015. Filed under Technical
Tags: rust, os, programming

NOTE: You can see the source code for the shell on GitHub.

After turning in my first Operating Systems homework, I remembered why people invented high-level languages. C’s beautiful and powerful and really bare-metal, but it’s also insanely easy to shoot yourself with it. And because I haven’t programmed in C for 2 or 3 years, I probably made every stupid mistake imaginable. Accidentally returned a pointer to something allocated on the stack? Have a segmentation fault. Accidentally dereferenced a null pointer? Have another segmentation fault. Forgot that strings need to end with a null-byte? Have another goddamn segmentation fault.1

Obviously, I’m making a much bigger deal about this than it deserves. After all, most of these mistakes were stupid things that any competent C programmer would laugh at. But I can definitely imagine that C’s gleeful willingness to assist program suicide causes a lot of problems. And I really believe that most of these things could and should be caught by a stronger type-system. For example, why don’t we use different types for heap-allocated and stack-allocated data (with suitable interoperability abilities)? Why doesn’t the compiler figure out when I need to check for a null pointer and make me do it?

After working with Haskell’s type system, C’s type system seemed almost laughably weak. Null pointers made me fall in love with Haskell’s Maybes all over again.

I also realized that Python has made me incredibly lazy. I gained a whole new appreciation for how abstracted we are from what actually goes on under the hood. For example, when I was munging around with the $PATH variable, I can write:

1
2
3
4
5
6
def path_remove(to_remove):
    path = os.environ["PATH"]
    new_path = ":".join(part
                        for part in path.split(":")
                        if part != to_remove)
    os.environ["PATH"] = new_path

Nice, short, readable. In C, I have to write something like this instead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void path_remove(const char * to_remove) {
    char * path = getenv("PATH");
    if (path == NULL) {     // $PATH not found
        return;
    }

    char * new_path = malloc(strlen(path) + 1);
    if (new_path == NULL) {
        print_error();
        return;
    }

    bool first = true;
    char * end = new_path;  // for fast stpcpy
    for (char * part = strtok(path, ":");
                part != NULL;
                part = strtok(NULL, ":")) {
        if (strcmp(part, to_remove) == 0) {
            continue;
        } else if (!first) {
            end = stpcpy(end, ":");
        }
        first = false;
        end = stpcpy(end, part);
    }

    if (setenv("PATH", new_path, 1) == -1) {
        print_error();
    }
    free(new_path);
}

Much uglier.

Rust to the rescue

Luckily, there’s a better way (aside from doing it in Python). Rust to the rescue!

Rust is a language I’ve been following for a while now, but I’ve never actually written any code in it before. The best way I’d describe it is C++, but with Haskell’s type system. On one hand, you have to manage your own memory, but you can also write things like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn path_remove(to_remove: &str) {
    match std::env::var("PATH") {
        Err(e) => println!("Error: {}", e),
        Ok(path) => {
            let new_path = path.split(":")
                .filter(|s| *s != to_remove)
                .collect::<Vec<_>>()
                .join(":");
            std::env::set_var("PATH", new_path);
        }
    }
}

Notice that anonymous function inside the filter. It looks much more like Python than C. It’s also very functional (as in functional programming): notice the match statement, the type inference of path, and the anonymous function with the filter. Although Rust is certainly a systems programming language, it’s got much higher level abstractions than C.

What I need to learn more about

Although Rust has converted me, I definitely hit some speed bumps. These are the things that looked complicated enough that I didn’t bother learning them for this toy project:

Still, all in all, I’m very pleased with Rust. Next time that I need high-performance non-numeric code, Rust will certainly be the first place I turn.


  1. As you can guess, I’m not a big fan of segmentation faults. Thank god for Valgrind. ↩︎