An Intro to the Rust Programming Language

Here at ACV Auctions we use many programming languages to deliver our services to our customers, whether it be Python, Java, C#, Go, JavaScript, Kotlin, and Swift to name a few.

All of these languages have their tradeoffs in how they allow a software engineer to solve problems. As we grow as software engineers in our career sometimes we like to look at different languages to see how a language we’re unfamiliar with makes its tradeoffs.

Rust is one of the languages I’ve been diving into lately, and has some unique tradeoffs, and I’d like to briefly introduce you to the language.

I’d like to share this quote about thinking about learning new programming languages that I think hits the nail on the head from Paul Graham, in his Weird Languages essay:

So if you want to expand your concept of what programming can be, one way to do it is by learning weird languages. Pick a language that most programmers consider weird but whose median user is smart, and then focus on the differences between this language and the intersection of popular languages.

Let’s learn more about about this weird language. Let’s go!

What is Rust?

Rust is a compiled programming language that was created by Graydon Hoare at Mozilla Research with contributions from others as well. It’s a general-purpose programming language designed for performance and safety.

It has been growing in popularity steadily since 2010.

Why Should I Consider Rust?

I first heard about Rust from those in the JavaScript community who were building tooling for the JS ecosystem. At first it was just a few people mentioning it here and there, and today there are many companies trusting the Rust programming language with an array of problems, to name a few there are Figma , Dropbox , Discord , NPM , Atlassian , Amazon , Facebook , Apple and more .

I finally took the plunge to start learning Rust after last year’s Stackoverflow’s annual survey came out. For the fifth year in a row Rust has held the “most loved language” title among other languages. So after seeing this, I had to find out what all the fuss was about.

From a tradeoffs perspective Rust as some interesting ones.

  • Designed for performance and memory safety
  • fearless concurrency
  • Guarantees memory safety using a borrow checker to validate memory references and introduces the idea of Ownership
  • Efficient and portable as idiomatic C++ without sacrificing memory safety
  • Does not use garbage collection, reference counting is optional
  • variables are immutable by default, unless you specify otherwise
  • uses structs extensively
  • uses traits to defined shared behavior
  • control flow operator called match that is powerful
  • has generic data types
  • concept of lifetimes to prevent dangling references
  • smart pointer data structures that act like a pointer but also additional metadata and capabilities
  • low-level unsafe Rust for the times that you need it

All of these concepts that make Rust very unique among programming languages is outside the scope of this post, but I’ve included links to some of the concepts to the official Rust Language book online for the reader to dig further into.

How to Get Started

My recommendation when learning a new programming language, and I think echoed by Paul’s quote above is that if you already know one language, use that one and compare it to the new language as you go.

For instance, if you know the Go programming language, maybe think about as you’re programming in Rust, why does Go have a nil type? Why doesn’t Rust have a nil type? Is there a tradeoff here that the language designer made? How do I have to think about that as I program in this new language? How will that affect how I think about nil types when I’m programming back in Go?

These are excellent kinds of questions to ask yourself as you compare and contrast the language that you know, with the one that you don’t.

The second thing that I recommend is that you should take a domain that you do know, in the language that you know, and build that same thing but do it in Rust. Is it less code to do it in Rust? Is it more? Why? Does one version have better performance than your original one? Why?

If you happen to be someone who programs APIs, try writing a server that provides an API in Rust. I think you’ll surprise yourself in how much more you’ll learn if you take a domain you understand, and try to write that in Rust.

Getting Started

Rust is a compiled language, but if you don’t want to install it on your local machine right away and just want to play, you can always check out the Rust Playground online. It’s a great way to get a feel for the language.

If you’re ready to dive in and install it on your machine here’s what you do:

Once the installation is finished, you’ll see that it installs these things:

  • rustc - the Rust compiler (think gcc, roslyn, clang)
  • rustup - the toolchain multiplexer (think rbenv, pyenv, nvm)
  • cargo - the Rust build tool and package manager (think rubygems, pip, npm)

Looking for a good Rust IDE? Then you should try VSCode or Intellij, they have really good support built in. You can use any IDE whether it be Atom or Vim, just know that you’ll have to go searching for plugins to get it going.

A Quick Introduction to Rust

As I’m learning Rust, I’ve built a few things. One thing is a simple CLI that will play a music file on your machine on loop. Lets walkthrough this simple Rust program.

Cargo and File Structure

If you’re creating your first Rust project you can do cargo new <project_name> like I did to start this project.

The directory structure will look something like this:

├── Cargo.toml
├── src
│   ├── main.rs
│   └── play_loop.rs

There is a Cargo.toml that determines the name of your project, the version of your project, the author, the edition of Rust that it compiles to, and the dependencies that you may want to include. These dependencies are called crates in the Rust world.

When you run cargo build it will install dependencies and build your project.

Defining Dependencies

I installed 3 dependencies as you can see under the [dependencies] block:

[package]
name = "looper"
version = "0.1.0"
authors = ["Kevin B. Ridgway"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bin]]
name = "looper"
path = "src/main.rs"

[dependencies]
color-eyre = "0.5.11"
play = "0.5.3"
structopt = "0.3.21"

The Main Program

Rust programs have a main.rs which is the first file that gets executed and by default it has a fn main() main function that is the first thing to bootstrap the program. fn is pronounced fun by the way, because Rust is fun. 😉

At the top is the use keyword that pulls in Rust crates and their functions after the ::. By default functions are private so you need to use the pub public keyword in your other module files to be able to use them in your main.rs.

This main function returns an optional Result type. The type definition for functions are defined as such: fn main -> Result<()> The arrow indicates that it’s returning something, and after that defines what type it will return.

The match flow keyword allow you to match on a variable, in this case, since I’m matching what’s passed into the CLI opt.cmd, and if command matches play, then call the play_file public function from play_loop and pass in the String that was passed in via the CLI.

From the CLI it looks like this:

looper play --url "/your/long/path/here/play_that_funky_music.mp3"

// main.rs
use color_eyre::eyre::Result;
use structopt::StructOpt;

mod play_loop;
use play_loop::play_file;

/// A CLI tool that plays songs on loop so you can get in the zone
#[derive(StructOpt, Debug)]
#[structopt(name = "looper")]
struct Opt {
    #[structopt(subcommand)]
    cmd: Command,
}

#[derive(StructOpt, Debug)]
enum Command {
    /// play something on loop
    ///
    /// This command will play the file you give it
    /// on a loop until you exit the program
    Play {
        /// play a specific file
        #[structopt(short, long)]
        url: String,
    },
}

fn main() -> Result<()> {
    color_eyre::install()?;
    let opt = Opt::from_args();
    match opt.cmd {
        Command::Play { url } => play_file(&url),
    }
}

The Play Loop Module

I define a module called play_loop which is referencing the play_loop.rs file. If you look at the play_loop.rs file you’ll see that it pulls in two crates at the top and then defines two functions. One is public the first one, and the second one is private as it does not have the pub keyword.

The play_file function passes in the url or filename of the file you passed it on the CLI, it prints the filename to the CLI using the separate private function print_playing_filename and then using a for loop it calls the play function from the play crate that I pulled in via Cargo.toml. After it’s done it returns an Ok() optional type (hint: this is why Rust doesn’t have nil).

// play_file.rs
use color_eyre::Result;
use std::path::Path;

pub fn play_file(url: &str) -> Result<()> {
    print_playing_filename(&url);
    for _i in 0..=10 {
        play::play(&url)?;
    }
    Ok(())
}

fn print_playing_filename(url: &str) -> Option<()> {
    let path = Path::new(url);
    let filename = path.file_name()?.to_str()?;
    println!("Playing {} on loop", filename);
    None
}

And that is how this simple CLI program is put together using Rust. You can check out the full source code here: GitHub - program247365/looper: A simple Rust CLI program to play music files on loop.

Conclusion and Community

I hope this encourages you also to check out Rust. The Rust community is a great group of folks. You can join the Public Discord community for Rust like I did, and get hands-on help from folks in the #rust-beginners channel. A kind Rustacean (that’s what we call ourselves in the Rust community, see Rustacean.net) even sent me a pull request and helped explain to me what I was doing wrong when I couldn’t get my CLI to compile.

It’s a great time to learn Rust, join us!

More Rust Resources