If you have a programming language background like mine, the following concepts are essential in order to start writing Rust productively. This list of topics is not meant to make you a Rust expert. It’s meant to help you write non-trivial Rust programs in the least amount of time possible for you.

(For those that know Rust well, I’m synopsizing. I know there’s more to these topics, hence the links. For new Rustaceans, after you read my synopsis, read the information at the links. Steve Klabnik’s work on the book is one of the best things about learning the language.)

String vs &str - https://doc.rust-lang.org/stable/book/strings.html

You’re probably thinking, “Strings? What’s the big deal about strings?”

I did too when I started seeing various questions about strings in Rust on the various Q&A sites.

In general, modern languages have really good string support and the API around strings for every modern, mainstream language has coalesced into the same handful of operations (split, trim, replace, and so on). If you learn strings in one language, you’ll probably be able to transfer the majority of your string knowledge to another language.

That’s not entirely the case with Rust. That is, the same API exists but there are two string types that serve very different purposes. In short, the difference comes down to:

“String” is heap allocated and can be modified. “&str” is stack allocated (this is not correct, see edit below) and cannot be modified. If you want a “&str” string to live beyond the current scope, convert it to a “String” (.to_string()). Otherwise leave it as a “&str”. It should be noted that converting to “String” is costly.

At first I was thrown off by the design decision to have two string types. But as I’ve used them I’ve come to accept that having two string types is no different than having multiple numeric types that serve different purposes.

EDIT 12/18/2015: It was pointed out by Manish Goregaokar and again on Reddit that my description of &str is not accurate. Thanks for the corrections. I’m still learning the language also.

2. Mutability — https://doc.rust-lang.org/stable/book/mutability.html

This is the easiest topic to talk about. It’s a simple concept but if you’re not coming from one of a handful of languages (e.g. Scala) you’re going to be surprised when the compiler complains that you’re trying reassign a variable (a binding in Rust nomenclature).

Mutability is a scourge. Most programs should avoid mutating memory at all costs. But Rust is aimed at systems programming (although there’s no reason it can’t be used for any programming) and systems programs must be memory efficient.

All bindings are immutable by default. You must explicitly specify which bindings (again, variables) are mutable.

That is, if you don’t specify a binding as mutable, once you assign a value to that chunk of memory, you can’t change that chunk of memory.

Most of the time you should work with immutable bindings but when you need it, mutability is available.

3. Traits - https://doc.rust-lang.org/stable/book/traits.html

Rust does not support the traditional notion of inheritance as it exists in languages like C# and Java. There is no class Dog is a subtype of class Animal which means instances of Dog can do all the things instances of Animal can do and Dog instances can be used as if they are Animal instances.

Rust supports what is colloquially known as duck typing. I’ve read enough debates about the correct comp sci classification for Rust’s type system to know that if I name one, I’m going to get comp sci’ed to death.

No thank you.

If you just want to give Rust a try, and get something non-trivial running, you don’t need to know the comp sci level information.

The easiest way to understand the type system is that most of the time you’re going to work with an instance of a specific struct or a trait object.

Working with instances of a specific struct is no different, in practice, than working with an instance of a class as in other languages.

However, any struct can also provide an implementation of any trait or multiple traits. Which means that any instance of a struct that provides an implementation of a trait can be used anywhere that trait is required. Instances of structs passed as implementors of traits are referred to as trait objects.

In other words, if it implements a trait, it is that trait when required.

EDIT 12/18/2015: I knew there was no way to write about the type system without someone commenting. But Manish Goregaokar made a good point here. I stand by my description however.

4. Pattern matching - https://doc.rustlang.org/stable/book/patterns.html

I’m very fortunate to come to Rust after learning Scala. Things like traits (although a little different in Scala) and some of the functional aspects of Rust were much easier to understand because I write Scala daily. Pattern matching is another of those things.

Pattern matching is like case statements one would encounter in other languages but pattern matching is super charged.

One can match on tuples, enums, builtin types and custom types. The pattern can include all or some of the values of a type’s members at the moment of the match or further refine the pattern using guards, if statements that ensure members bound through the pattern have specific values.

Pattern matching is extremely expressive and can replace most if statements with clean, more understandable code. In other words, you won’t see a bunch of if’s nested if you use pattern matching well. Pattern matching is also important to Rust error handling.

5. Error Handling - https://doc.rust-lang.org/stable/book/error-handling.html

I’m going to keep this one short because it’s probably the most boring part of the language that you must understand. The best section of the book is the error handling section.

Forget exceptions. They don’t exist.

Learn to love the Result type and pattern matching.

Don’t get used to unwrapping (.unwrap()). With a few exceptions it’s almost always wrong.

Go read the error handling section of the book.

6. Ownership, References/Borrowing, and Lifetimes - https://doc.rust-lang.org/stable/book/ownership.html, https://doc.rust-lang.org/stable/book/references-and-borrowing.html, https://doc.rust-lang.org/stable/book/lifetimes.html

TFBC = THE FUCKING BORROW CHECKER

Even if you’re not into working blue, eventually you will refer to the compiler code that validates ownership, borrowing and lifetimes as THE FUCKING BORROW CHECKER.

The borrow checker is the Rust feature that you will see talked about the most. And for good reasons. It’s Rust’s killer feature. But it comes at the cost of a very steep learning curve. As I mentioned earlier, I still fight TFBC almost every time I write non-trivial Rust. But I’m getting better and winning the borrow checker fights quicker every day.

Every single piece of memory used by your Rust code is owned by a single binding. In order to do anything interesting with that memory, the binding that owns the memory must lend the memory to some other binding or some other binding must point to a copy of the contents of the memory of interest.

In addition, every single piece of memory has a lifetime tied to the scope in which is was created or to which ownership was transferred.

These features provide the means by which Rust can (almost) guarantee memory safety. No more null pointer exceptions, dangling pointers, or seg faults one has to deal with in other low level languages.

The borrow checker runs during compilation and performs the analysis required to report compilation errors related to the unsafe use of memory. So, there’s no runtime cost associated with the ownership rules.

The borrow checker, like valgrind before it, is that friend you love but very rarely says anything positive. And more often than not, that friend is right which makes it worse.

Ownership, references, borrowing, and lifetimes are the subjects that will prevent new writers of Rust from being productive quickly. One has to understand them and it takes work. One can’t fake out the borrow checker. But I’m finding that it’s worth it. In fact, I’ve come to understand how truly bad my C++ code was back in the day because of TFBC.

Rust is a relatively small language. But it takes time and hard work to get to the point where you feel like you’re moving as fast as you may in other languages. Were I coming to the language today, this list would have helped me focus on the things I absolutely needed to know right away.

PS - This post started with the following tweet.

I’ve since considered the topic a little more and have come to the conclusion the second tweet was a really dumb statement by itself.

That tweet should have started with, “IF YOU HAVE A PROGRAMMING LANGUAGE BACKGROUND LIKE MINE…”

Rust is very hard to learn as far as programming languages go. Even people like me, who have been programming in multiple languages longer than we care to admit, struggle with the language for a non-trivial amount of time. I’ve been writing Rust on and off for nearly 18 months (almost daily for the last two months) and I still fight the borrow checker and have to look things up a lot. It’s been a long time since I’ve struggled to learn a language as I have with Rust. C/C++ might be the last and that was a long, long time ago.

My point is that I don’t want the second tweet, or this post, to make someone feel if they don’t get Rust right away they are never going to get Rust or are not as smart as others that seem to get Rust the second they write their fist line of Rust code. I bet there are very few people who are able to be productive writing Rust without putting in a lot of work.

I don’t like resume blog posts. However, in this case, given the stance I’ve just conveyed, I think it’s appropriate to provide the details of my recent programming language background.

In the last five years I’ve put applications and services into production written in the following languages.

Scala

Java

JavaScript

Go

C#

Ruby

Python

Most of those languages were learned during that same five years. The exceptions are C# and Java which I’ve known much longer. In addition, I cut my teeth on C and C++ a long time ago and I’m working on new, production C++ code currently. Plus I’ve tinkered around with a few additional languages over the years.

My point being that I’m conversant in a lot of languages and there are shadows of many of those languages that fall upon Rust. Which is probably why I felt like learning “5 or 6 #rustlang concepts” before being productive was reasonable.