DEV Community

Stoian Dan
Stoian Dan

Posted on • Edited on

Why Rust is a "modern" programming language

Introduction

Rust is a compiled, statically typed, multi-paradigm, modern programming language designed for memory safety and concurrency, without needing a garbage-collector.

What does "modern" mean?

Granted, I've used the word modern, precisely to capture attention. The term is subjective, and many of the features that I consider "modern" are actually concepts borrowed from other languages (more on that later).
However, it's the selection of features and the cargo ecosystem, providing support for dependency management, building, testing, etc., that make the language truly modern.

Statically typed

Let's start with the non-controversial. Being statically typed means data types are known at compile time.
Rust is trying to bring the compiler on your side, by catching as many problems as possible, before your program runs. Knowing data types at compile time allows it to do just that. But that doesn't mean the experience needs to be verbose:

let x : u32 = 10; // type explicitly declared as an unsigned 32-bit integer  
let y = x + 5; // y's type is also u32, but it's inferred by the compiler
Enter fullscreen mode Exit fullscreen mode

Memory safety

Rust wants to make sure that you write correct programs.That won't dereference dangling (invalid) pointers, access uninitialized memory (more on this later). Similar to C++'s RAII when things pass out of scope, memory gets freed for you:

    use std::fs::File; // "import" from module responsible for file-system interaction
    {
     // grab a resource 
    let fileHandler = File::open("foo.txt")?; 
    //... do stuff with out file
    } // when fileHandler goes out of scope, memory is freed
Enter fullscreen mode Exit fullscreen mode

What's also nice is that Rust won't allow you to reference names that are out of scope:

  fn add(first : u32, second : u32) -> &u32 {
      let result = frist + second;
      return &result; // error
  } // result gets freed here
Enter fullscreen mode Exit fullscreen mode

result will get deallocated when execution get out of add's scope, so returning a reference (via & operator) to it is invalid. (this code would also require something called reference lifetimes, that won't be covered here.)

"Modern"

Here's my favorite part. As previously said, many of these features are inspired from other languages. Rust has been influenced by many languages, learning from their mistakes and not repeating them. It borrows features from functional programming, but the following selection makes it shine, providing support for a multi-paradigm development.

Destructuring

You might be familiar with this from JavaScript. Say we have a 3D point, Point3D:

//a so called "tuple struct"
// with three unsigned integers corresponding to x,y,z 3D axis
struct Point3D(u8,u8,u8);
Enter fullscreen mode Exit fullscreen mode

We can retrieve only the information we are interested in, by destructuring

 let three_d = Point3D(4,6,3);
 let (x, y, _) = three_d;
 println!("The 2D cooridnates are x: {} and y: {}", x, y);
Enter fullscreen mode Exit fullscreen mode

Pattern matching

This is just precious. Let's say you have a Person struct, described by name and age:

struct Person {
    name : String,
    age  : u8
}
Enter fullscreen mode Exit fullscreen mode

With pattern matching you can do things you've always dreamed about with a traditional switch statement:

let p = Person { name : String::from("Jennifer"), age : 23 };

    match (p.name.as_ref(),p.age) { // match operator pattern matches the result, much like a switch statement, only more advanced
        ("Jennifer",_) => println!("Oh, Jenn it's you, I need help with the cake, dear."),
        (name,minor) if minor < 18 => println!("Dear {}, there's some RobbyBubble for you.", name),
        (name, major) if major >= 18 => println!("Dear {}, there's some champain for you.", name),
        _ => {} // default case
    }
Enter fullscreen mode Exit fullscreen mode

If you noticed, here we also used tuple destructuring. At the match statement, where we created a tuple paired with a person's name and age.
Pattern matching is really powerful and has application in the language other than the match statement, like if let.

Immutability by default & shadowing

Variables in Rust are immutable by default. Their state is guaranteed not to change. A feature coming from functional programming, in
languages like Haskell or Racket.

C#/Java for example, don't have this feature. The problem with having objects mutable by default is that you have to make sure passing references won't mess up their internal state.
Immutability can prevent annoying debugging situations like asking yourself why suddenly that list is empty? Why that bool you could have sworn should be true is actually false. ๐Ÿ˜‘

This is usually solved by good encapsulation, but that's not always possible. Especially when working with third party code that's not in your control.
Also, Rust has a neat feature called shadowing, allowing you to do something like:

 // mutable variable explicitly marked so with the "mut" keyword  
  let mut p = Person{name: String:from(""), age : 24};
  // read the name
  println!("Please enter your name: ");
  // pass mutable name to be read from stdin
  std::io::stdin().read_to_string(&mut p.name);
  let p = p; // "shadow" old p name, and redefine it as immutable
...
Enter fullscreen mode Exit fullscreen mode

From the last statement, we know nothing will messes up the state of p, because from there on it's immutable. โœ”

No null!

The null reference, or lack therefor of is another reason why I call Rust "modern". Tony Hoare called null reference, his

billion-dollar mistake

Rust simply doesn't have null or any similar keyword. It describes the absence of a value by an optional (see optional type). Achieving this via generics. For example:

  let can_have_number : Option<u32> = Option::Some(6);
  match can_have_number  {
         Some(x) => println!("We have the number: {}",x),
         None => println!("There is no number to print!")
 }
Enter fullscreen mode Exit fullscreen mode

We can wrap any data of any type inside an option, obliging the user to handle both cases. When there is data, wrapped inside a Some, or when there isn't any. To better understand this, we'll talk about generics and enums.

First class enum support

If you've ever used an enum, you might be familiar with the C style enums.
But in Rust, besides serving as some symbol or integer value enums can have associated values with them.
The Option type you've seen earlier is an enum, that could be generically defined as:

enum Option<T> {
    Some(T),
    None
}
Enter fullscreen mode Exit fullscreen mode

Option has two possible values Option::Some(T) and Option::None.
Some describes the presence of a value. None describes the lack therefor of.
Notice, for None, we have no value associated, it's sufficient to say: "there's nothing here". For Some however, we're not just saying "there is, there is something in here". We're also actually storing and providing the data.
Can you see how that is another use case for pattern matching?

Traits

Traits are like interfaces in Java/C#, they allow for polymorphism, by making structs implement functionality and being able to refer to a struct type, as an interface type.
They differ in that traits can be implemented on types you have no control over.

Imagine using a third-party JPEG library for your very own photo viewer application.
The program, already supports PNG's, Bitmaps and other formats, that use a trait named Photo. If only that JPEG library had also known about it!๐Ÿ˜‘
Well guess what, in Rust you can implement the Photo trait upon the JPEG structure provided by the library!
You can even implement self written traits upon built-in Rust data types like u8, String, etc. โœ”

Conclusions

Together with plenty of other features like borrowing, multi threading and lots of zero-cost abstractions, Rust is a free/open-source modern language.
Developed at Mozilla Foundation, it's enabling all kinds of software, from so called system-programming (embedded, kernel programming) to web apps, via Web Assembly.
If you're interested in learning Rust, I recommend the free Rust book.

Top comments (5)

Collapse
 
tabnine profile image
Tabnine

Great article Dan. As a matter of fact, Tabnine AI code completion was written in Rust :) Did you get the chance to ever use Tabnine?

Collapse
 
stoiandan profile image
Stoian Dan

I've didn't heard of it, will take a look. Thank you!

Collapse
 
tabnine profile image
Tabnine

Thanks! Would love to hear how it goes :)

Collapse
 
lannex profile image
Shin, SJ • Edited

Nice. This is a good article to introduce Rust first. So Can I translate it into Korean? I'll make sure to leave a source.

Collapse
 
stoiandan profile image
Stoian Dan

Sure, I'd be honored! Glad you liked it! ๐Ÿ˜€