What is Rust and why would I wanna learn it?

Rust is a relatively new language, conceived 2010 which makes it a good 10 years old. Poised as a memory-safe alternative to C++ with no runtime overhead, there’s no garbage collection unlike Go or Python and ownership is predicted at compile-time by default. The biggest downside I was prepared for is one of the steepest learning curves for a newcomer to a programming language. That’s what I’d heard from many people I talked to before. So perhaps this is where I’d compare it to Perl in spirit which I started using two and a half years or so ago and which was very rough in the first couple of months.

The idiosyntaxies of the language

Some say Rust has a C++-like syntax. I found myself struggling hard to read any code at first. There’s a lot that’s quite unlike other languages I’m familiar with. Variable declarations remind me of my childhood friend Pascal when it’s in a function signature or a struct, but also C++ when the type is inferred from a constructor. Module imports feel like Perl. Function return values and match statements are probably the most alien aspects starting out just because they don’t quite translate to anything I knew. To round it off semi-colons are used in some but not other contexts so I knew I was in for a bumpy ride. Well, that’s not stopped me before so let’s have a closer look!

use rand::Rng;
use std::fs::File;
use std::io;
use std::io::prelude::*;

#[derive(Debug, Clone)]
struct Feline {
    name: String,
    age: u8,
}

fn main() -> Result<(), io::Error> {
    let cat = Feline {
        name: "Aladin".to_string(),
        age: rand::thread_rng().gen_range(1, 101);
    };
    let mut file = match File::open("dogfood") {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    let mut buffer = String::new();
    file.read_to_string(&mut buffer)?;
    drop(file);
    feed(&cat, &food);
    Ok(())
}

fn feed(cat: &Feline, food: &String) {
    println!("{} is having {} today", cat.name, food);
}

Having had a glimpse at this little example snippet you can probably make a good guess. But if you’re anything like me you don’t actually know what the syntax means and couldn’t reproduce it easily. For the reader’s benefit how about a Python version?

#!/usr/bin/env python
import os, random, sys

class Feline:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def feed(self, food):
        print ('{name} is having {food} today'.format(name=self.name, food=food))

cat = Feline('Aladin', random.randint(1,12))
try:
    with open('dogfood') as food:
        cat.feed(food)
except FileNotFoundError as e:
    print(e)

Even if you don’t know Python you will notice the lack of typing and how much more implicit the code flow is. What you don’t see until you try and run it, the Python interpreter doesn’t especially care that this code is valid. If you copy this code and make a mistake most likely it will execute fine but fail at runtime or produce a different result. Attempting the same with rust will give you an almost guaranteed error at compile-time!

Obviously Python is designed as an interpreted language. But even C++ would have less type-checking ahead of time compared to Rust.

So what’s this about dogs with no pedigree?

One of those odd keywords is mut. I can’t help but read it the same as the word for a dog with ambiguous parents. Or maybe it reminds me of an email client. Jokes aside, I’m talking about the mutable keyword. If you plan on making any kind of change to a variable at runtime you need it.

You pass it on, you no longer own it

Another thing you want to get used to is references. By default whatever you pass around is owned by the receiver. Trying to use the variable again in the original context is a compile-time error. Which actually gives you the confidence that you don’t have any extra copies or allocated memory that’s not freed.

Conversely you use & on both the sending and the receiving end to clarify that you need the variable in multiple places. Although it’s otherwise the same and you don’t need any special syntax for variables passed as references like you would in C++. That part I like because there’s no situation in which you can mix the two and I always found this odd with C++.

Strings and string formats

The two types of strings… or perhaps I should say strings and slices, this is something which contributes to the verbosity of Rust and I’m sure you already noticed it in my earlier example.

"Aladin".to_string(),

let mut buffer = String::new();

I feel like this is something that’s solved by special-casing the primary string type in many languages so that the syntax for both the constant and the mutable string are the same. And I have some appreciation for making this explicit. I don’t know if I like or hate it yet. It might grow on me.

What’s a prelude if not the beginning of epic music?

A prelude in Rust is where magic like drop, Copy, Option, Result, String and operators are defined. These are what you’d consider built-in types in other languages. It’s not actually hard-coded in the compiler but there’s a bit of code that is added just before. Although it feels just like built-in keywords when you use them I felt it’s worth pointing out.

Cargo cult programming

The tooling feels really good. If you’ve done any Go, that’s what it feels like and in a good way. You have standard ways of doing things without relying on third party tools or custom scripts.

cargo run
cargo doc --open
cargo build
cargo install cargo-expand
cargo fmt
cargo new myproject

I’m not going to explain all of these in detail since they’re quite intuitive. It’s worth highlighting that run compiles and runs a project’s executable even when you don’t know where and what it is and fmt makes all code conform to the recommended coding style even if your code is slightly broken. The compiler will demand that your code is predictable so it’s relatively difficult to make a mess - besides being confused until you get used to the very explicit syntax errors of course.

It’s not unusual for a typical package to pull in 150-200 other packages. Which is not necessarily going to make things slow but you can probably forget about keeping track of your dependencies in obscene detail.

How do I like it after a week or so?

I used the SUSE Hackweek to get into Rust by use of the official book. That week was good to get down the basics of the syntax although I’m sure I’ve plenty of things to learn. The extreme verbosity will probably grow on me over time. I can’t really say that I love the language or that I hate it at this point. But I wasn’t expecting to. As I get more exposure to it I’ll figure out what it’s most useful for and especially how I can use it best. Any tool is only as good as its use case.