First Lines of Rust

Nov 18, 2020 📰 5 minute read

After ~8 years of Go as the end-all-be-all of programming languages in my life, I started writing Rust code for a real project, not just as a hobby.

The project, briefly put, is a Kubernetes system to let people easily and quickly deploy HTTP-based containers to production with a CLI. There is more to the story than that, but I don’t want to stray from what I’m excited about. I’m really learning a new language after all this time, from the ground up [1].

Unlearning Go is Hard

Lots of people come to the Go community and start writing Goava or Gython (Java-like Go or Python-like Go). I usually spend time with many of them helping un-teach that language so that they can absorb Go and build up the cognitive “muscle memory” that they need to be productive in the language.

Now, the tables have turned and I’m experiencing the same thing with Rust. One of the important first steps I’ve taken is accepting that I will still “know” Go even if I “unlearn” it so I really get productive with Rust. I’ll always be a Gopher.

Similarities to Go

As I progress, I’m writing down some cognitive and structural similarities I see in the two languages. That’s helping me make the transition from the Go way to the Rust way. Both are modern, systems-oriented programming languages so they view the world similarly and as such, they’ve landed on a few similar and important features.

Memory Safe

Go uses garbage collection and Rust uses the ownership concept, the borrow checker and (opt-in) reference counting, but both languages give you confidence at compile time that you won’t access invalid memory. They also work hard to help you ensure that memory will be cleaned up as expected.

Batteries Included

I was going to entitle this section “Built in Tests”, but more is built in than just tests. Both languages ship with almost all of the tools needed to build modern software. Go has the go CLI and Rust has cargo. In both cases, you run an installer to get a built in test framework, extensive standard library, dependency management (in Go 1.9 and up!), and more.

I can’t understate the effect these out-of-the-box tools have on the community. Anyone can come to the language, run an installer, and be ready to write production level code. Other language ecosystems make you learn – and sometimes even choose from – a wide variety of tools before you write your first line of code. That’s a big barrier to entry for someone new to the language and can turn people off.

Both ecosystems do have other tools you can learn, but you can be productive with what comes out of the box.

Errors are Values

One of Go’s hallmarks is that “errors are values”. In practical terms, that means that there is no exception system and the language makes you check any error that a function might return.

Rust also lacks exceptions, but has a more expressive type system [2]. It usually represents errors using the Result<T, E> type [3]. Many people use match statements to “pull” values out of the Result (you can do it in other ways too), and Rust has an exhaustiveness checker that effectively forces you to check every error.

Panic Only in Unrecoverable Situations

Go has a panic() function and Rust has a panic!() macro. They look almost the same and they have approximately the same purpose; if there is no way to recover and your program can’t continue under any circumstances, panic-ing will exit immediately with a stack trace (optionally in Rust).

Polymorphism

Polymorphism is an umbrella term to encompass many features in a programming language and Rust and Go provide a similar subset of them.

In Go, you can create an interface{} that specifies methods that a concrete type can implement. Any concrete type that implements them automatically adheres to that interface, without specifying so. You can use any concrete type where the interface is expected.

Rust has traits, which are very similar. You can define methods in them, and a default implementation for those methods as well (similar to an abstract class in C++).

One big difference in this area is that Rust requires that you explicitly state that a concrete type is implementing a trait. That small difference has a big impact in large codebases because with Go, you don’t have to add a dependency to your app just to implement one of its interfaces [4].

More?

I’m a month or so into building this app and becoming more productive by the day. I’ve been writing Go for a long time and I’ll naturally draw more parallels to Rust as I progress. So far, I’m enjoying the process and looking forward to the future 🚀.


[1] I’ve learned a few languages ad-hoc over the past years, but not from the ground up, and that’s why this is exciting for me [2] Go has plans to add generics to the language soon, but the proposed design is different from Rust’s generics design and it lacks features that Rust has. [3] Since Result is a regular enum in the standard library, it’s only the de-facto standard for error checking; you don’t have to use it as such [4] Go is sometimes credited with allowing “duck typing” for this reason, but the feature is closer to structural typing