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
Maybe
s 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
match
s 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. ↩︎