I often like exploring a topic in great depth and writing about my thoughts and experiences as I go along.

This is more of an extended essay than an article to be read in a single sitting. Feel free to read it one piece at a time, or just skip to the bits that look interesting.

Here, have a Table of Contents:

When you are coming to Rust from another language you bring all your previous experiences with you.

Often this is awesome because it means you aren’t learning programming from scratch! However, you can also bring along bad habits which can lead you down the wrong rabbit hole or make you write bad code.

The code written in this article is available on the Rust Playground using the various (playground) links dotted throughout. Feel free to browse through and steal code or inspiration.

If you found this useful or spotted a bug in the article, let me know on the blog’s issue tracker!

This one is a pet peeve of mine.

In most C-based languages (C, C#, Java, etc.), the way you indicate whether something failed or couldn’t be found is by returning a “special” value. For example, C#’s [String.IndexOf()](<>) method will scan an array for a particular element and return its index. Returning -1 if nothing is found.

That leads to code like this:

string sentence = "The fox jumps over the dog";

int index = sentence.IndexOf("fox");

if (index != -1)
  string wordsAfterFox = sentence.SubString(index);

You see this sort of “use a sentinel value to indicate something special” practice all the time. Other sentinel values you might find in the wild are "", or null (someone once referred to this as their “billion-dollar mistake”).

The general reason why this is a bad idea is that there is absolutely nothing to stop you from forgetting that check. That means you can accidentally crash your application with one misplaced assumption or when the code generating the sentinel is far away from the code using it.

We can do a lot better in Rust, though. Just use Option!

By design, there is no way to get the underlying value without dealing with the possibility that your Option may be None. This is enforced by the compiler at compile time, meaning code that forgets to check won’t even compile.

let sentence = "The fox jumps over the dog";
let index = sentence.find("fox");

// let words_after_fox = &sentence[index..]; // Error: Can't index str with Option<usize>

if let Some(fox) = index {
  let words_after_fox = &sentence[fox..];
  println!("{}", words_after_fox);

Back in the 70’s, a naming convention called Hungarian Notation was developed by programmers writing in languages where variables are untyped or dynamically typed. It works by adding a mnemonic to the start of a name to indicate what it represents, for example the boolean visited variable might be called bVisited or the string name might be called strName.