First Impressions of Rust

First Impressions of Rust

Rust is a new-age programming language promising extreme speed without the dangers of using a low-level, memory-managed language like C. It's an extremely exciting language and gaining adoption very quickly, so much so that the HackerNews is regularly dominated by posts of people showing off their latest "existing tool but written in Rust" projects.

I'm going to have to assume a fair bit of background and programming knowledge here unfortunately because evaluating a programming language is only useful if you know other languages and can draw useful comparisons for your viewers.

Hello, world!

Rust comes with one of the best (included) build tools in the world: Cargo. It also comes with an excellent tutorial in the form of an online book. I don't think I've ever encountered a language so eager to help you get started.

That doesn't mean getting started is easy. My first programming language was Haskell, followed by Python. I wrote a small game in Elm back in 2015 and then wrote Java professionally for almost 5 years before going back to mostly writing Python. My personal projects are almost exclusively written in Python.

The switch from Python to Rust was jarring. Python's big appeal is that it is dynamically typed and reads like English. Rust is not that. It's statically typed and reads like an old systems language like C or C++. And that's on purpose: Rust's goal is to provide an alternative to tools written in those languages.

Memory Management

I've only ever worked with garbage collected languages. Rust is not garbage collected.

Rust attempts to bridge the garbage collected strategy and the manually-managed strategy by introducing a concept of ownership. When you pass a variable to a function, you need to decide if you're passing ownership or not. With that kind of information, the compiler can automatically add the calls to free memory after the variable is used for the last time. One way to think of it would be garbage collection, but determined during the compilation stage.

The problem is that this means some of the most basic for loops don't work in Rust:

let s = "Hello".to_string();
for i in 0..10 {
    // 's' is moved to 'y' and will not be
    // available after the first iteration
    let y = s + &i.to_string();
    println!("{}", y);
}

Thankfully the Rust creators thought of this and realized it might be easier to provide helpful error messages than deal with people hating their language:

error[E0382]: use of moved value: `s`
  --> src/main.rs:18:17
   |
16 |     let s = "Hello".to_string();
   |         - move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
17 |     for i in 0..10 {
18 |         let y = s + &i.to_string();
   |                 ^ value moved here, in previous iteration of loop

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0382`.

This message tells me that I've "moved" the variable s in a previous iteration of the loop, exactly as I spelled out in the commented code above!

This sort of error handling at the compile stage is nearly unheard of. How would it know that there would be more than one iteration in the loop? Rust's compiler is one of the most helpful I've ever used, right up there with Elm's.

For someone with basically no background in memory management, getting going in Rust was shockingly easy. I'd try to compile, it would fail, and I'd be able to figure out why I hadn't figured out ownership quite right without too much difficulty.

Types

I love statically typed languages. While my personal projects are in Python, that's not because I particularly like Python. I use Python because:

  • Basically every programmer knows Python
  • I don't have to think about my system design for tiny projects
  • I know how to get a shitty Python project off the ground in less than a minute

As soon as a project reaches any reasonable scale, I find Python (or any dynamically typed) codebases to be nightmarish. Maybe I'm not good enough to understand how things fit together, but I prefer compiled, statically-typed languages for larger codebases.

Rust's type system is more similar to Haskell's than Java's, which is surprising given its syntax is closer to most object-oriented languages. I've been routinely surprised that inheritance isn't possible in Rust. It's quite possible I'm just designing my codebase incorrectly if I'm using that kind of tool, but I routinely find myself thinking about types in this way.

Thankfully, this hasn't really been an issue. I'm sure I've made some rookie mistakes, but generally I find ways around the lack of inheritance. As picture in the heading of this post, I've been playing around with making a pre-commit clone in Rust. At first I struggled mightily converting Python idioms into Rust code, but about a week ago it clicked: Rust isn't Python and I shouldn't write my code like I would for a Python project. Since then I've been able to rethink the data model of pre-commit and have made some progress towards an operational clone.

Maybe in a month I'll consider the Rust type system the "obvious" system. Maybe I'll start thinking in Rust abstractions in the same way I used to think in Java abstractions. But for now, I'll just point out that the Rust type system feels more similar to Haskell or Elm than to C++ or Java.

Performance

I haven't noticed. Seriously.

One thing that's becoming pretty clear to the programming community is that unless you're working in resource-constrained environments, garbage collected languages are usually fine for performance.

It can be difficult to make this work in a world where Rust out-benchmarks Go by incredible margins, but this kind of performance doesn't correlate very well to real world applications. We rarely write code that is constrained by the language's ability to run operations on binary trees. In fact, we rarely write code that's optimized for performance at all!

It turns out most processors nowadays are good enough to give you decent performance on basically anything. The slowest part of the experience is actually your ability to write, read, and change code! If your team knows Python, you'll probably write a better, more usable program than a team trying to learn C++ or Rust to squeeze out some extra performance.

I have no complaints about Rust performance, but I really haven't noticed.

Verdict

Rust is coming. It's a supers popular language getting a lot of hype right now, and rightfully so. A lot of tools are being written or rewritten in Rust right now; many more than I've seen for other languages.

Currently there aren't very many companies writing Rust out there. Java and JavaScript still  dominate the job market, and Python is an assumed skill for most developers. That might be changing, but for now Rust is a great way to help you think a little differently about the code you're writing