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:
|  |  | 
Nice, short, readable. In C, I have to write something like this instead:
|  |  | 
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:
|  |  | 
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:
- Associated types. I have no idea how to make a function that takes an
Iterator<&str>. I feel like it should be the same as passing in, say,Vec<&str>, although probably implemented very differently. When I tried to pass an iterator in, I got a bunch of errors about associated types that I didn’t understand.
- Error handling. Right now, error handling’s useable but a little
irritating – lots of matchs on errors. This should definitely be (and probably is) easier.
- Type checking. While type checking is nice, sometimes it fails for the weirdest reasons. Not quite sure why.
- How to look stuff up. Rust has some really nice API docs, but there aren’t enough up-to-date blogposts or Stack Overflow questions to get a high-level overview of what to do. This was especially prominent with the subprocess handling.
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.
- 
As you can guess, I’m not a big fan of segmentation faults. Thank god for Valgrind. ↩︎