loading...
Cover image for That's so Rusty!

That's so Rusty!

imaculate3 profile image Imaculate Updated on ・3 min read

I've picked up Rust lately, like most people I couldn't resist the hype. There ought to be a reason why it is consistently StackOverflow's most loved language, why it is deemed the industry's best chance at Safe Systems and probably why I was able to get through the 500+ page book. Whether you like the language or not, there is no denying that Rust is interesting.

Most of Rust's defining features are not particularly new or unique: Functional programming, Object Oriented Programming, Metaprogramming, Absence of null, Memory management without garbage collector are among many that have been around way before Rust. Rust stands out in the way it has been intentionally orchestrated for memory safety and runtime performance. In this series I will highlight some of these features.

Let's start with null. What does null really mean? In most languages it stands for the void: Nothing, Invalid, Zero, Placeholder that can be substituted for any type. It was invented by Sir Tony Hoare in 1965 and has been popular since then. Rust is among very few programming languages that don't support null and for good reason.

To start with, Sir Hoare himself called a billion dollar mistake. As a C++ developer I know the pain of having to write null checks everywhere, and have lived through consequences of missing just a single check. The idea of avoiding bugs, crashes and security vulnerabilities sounds very appealing.

In place of null, Rust has the Option enum that is generic over any type. and has two variants Some and None. If there is a possibility an object of type T does not have a value, that is can be None, its type becomes Option instead of T. If we try to use the object as T directly, the compiler complains furiously. We can therefore never use objects or references that are not defined, eliminating an entire class of bugs.

Consider this simple C++ program:

#include <iostream>
using namespace std;

struct Wrapper {
    int value;

    void print() {
        cout << value << endl;
    }
};

int main() {
    cout << "Entrance" << endl;
    Wrapper *w = nullptr;
    w->print();
    cout << "Exit" << endl;
}

It compiles just fine but fatally crashes at runtime. Segmentation fault happens when we dereference a null pointer. That was easy to debug because its a short, simple program. That is not always the case, as complexity grows, pointers are passed around and assumptions are made, it becomes for this bug to slip to production where it may take days to debug.

Literal translation to Rust would look like this, but of course it wouldn't compile.

fn main()
{
    println!("Entrance");
    let w: Wrapper = None;
    w.print();
    println!("Exit");
}

struct Wrapper
{
    value: i32
}

impl Wrapper
{
    fn print(&self)
    {
        println!("{}", self.value);
    }
}

w can't be of Wrapper type and be None. If we can't assign a value to it, it should be of type Option which we'd have to unwrap to get the Wrapper object. One safe way is a simple if conditional that uses w if it is some value and ignores it otherwise. The version below does that and behaves correctly.

fn main()
{
    println!("Entrance");
    let w: Option<Wrapper> = None;
    if let Some(v) = w
    {
        v.print();
    }

    println!("Exit");
}

Although C++ has similar optional type Optional, with null still around, its advantages are also well, optional.

So, what is impression of Rust so far? Inconvenient for the greater good? more than simple syntactical difference? Over-zealous compiler? All of the above? True. Stay tuned as we explore more of what makes Rust, Rusty!

Posted on by:

imaculate3 profile

Imaculate

@imaculate3

Engineer, Runner, Life long learner

Discussion

markdown guide