Rust Programming Language: A Beginner’s Guide

apium_hub

Apiumhub

Posted on January 22, 2024

Rust Programming Language: A Beginner’s Guide

In our workplace, we took on a challenge as front-end developers to delve into Rust and explore how we can create web applications. The initial step was familiarizing ourselves with the language’s fundamentals by studying the documentation. Upon initiating my study of Rust, I recognized similarities between Rust and JS/TS, drawing these was important for me to facilitate a more intuitive understanding. I wanted to share my learning path, writing this article outlining my exploration of Rust from the perspective of a front-end developer.

The Rust programming language was originally developed by Mozilla for Firefox, and it is also used in major companies such as Facebook, Apple, Amazon, Microsoft, and Google. Notable projects like Dropbox, npm, GitHub, and Deno leverage Rust too. Also the compiler of Next.js “Turbopack” has contributed to a remarkable 94.7% increase in speed in Next.js version 14.

Why the Rust programming language is gaining popularity can be attributed to several key factors. Firstly, Rust is a compiled language that generates efficient machine code. This characteristic ensures that applications developed with Rust deliver exceptional performance. Moreover, Rustis highly reliable due to its compiler, which effectively prevents undefined behavior that might otherwise result in unexpected outcomes or crashes.

Another one is its memory efficiency. While many languages either manage memory automatically, like JavaScript’s garbage collector, or have complete control over memory management, as in C or C++, Rust introduces a unique approach called the ownership model. (We will get back to this topic later)

In the upcoming sections of this article, we will explore essential topics of the Rust programming language from the perspective of a front-end developer. These topics include data types, variables, mutability, functions, tuples, arrays, and structs, as well as references and borrowing.

Rust Programming Language

Data types

The contrast between JavaScript and the Rust programming language primarily manifests in their approach to data types. JavaScript adopts a dynamic typing system, while Rust employs static typing. In Rust, it is a must to determine the types of all variables at compile time, a characteristic that aligns more closely with TypeScript.

Every value in Rust is associated with a specific data type, and these types are categorized into two main groups: Scalar and Compound types. In contrast, JS/TS has a small set of data types such as numbers, strings, booleans, and objects. Scalar types in Rust include integers (both signed and unsigned), floating-point numbers, booleans, and characters, while compound types comprise tuples and arrays.

Integers

A notable distinction in data types is that, unlike JS/TS, Rust provides size-specific choices for integers and floating-point numbers. This allows you to precisely regulate the amount of memory allocated for each type. Consequently, Rust stands out for its memory efficiency and high performance.

Rust

Floating numbers

Rust offers two floating point types: f32 and f64, with sizes of 32 bits and 64 bits. The default type is f64, because it provides a similar speed to f32 offering more precision. It’s important to note that all floating-point types in Rust are signed.

let x = 5.8;
Enter fullscreen mode Exit fullscreen mode

Booleans

In Rust like in JS/TS, the Boolean type has two potential values: true and false. They are one byte in size, and it is denoted by bool.

let isRustReliable = true;
let isRustReliable: bool = true;

Enter fullscreen mode Exit fullscreen mode

Characters

Rust’s char type is four bytes in size. It specifically represents a Unicode Scalar Value, allowing it to encompass a broader range of characters beyond ASCII. This includes accented letters, different alphabet characters, emojis, and zero-width spaces, making Rust’s char type more versatile in handling diverse character sets.

let char_type = 'a'
let char_type: char = 'A';

Enter fullscreen mode Exit fullscreen mode

Tuples

A tuple groups together a variety of types into one compound type. Tuples come with a predetermined length, and once declared they are unable to expand or delete.

let origin: (i8, u8, f64) = (-5, 2, 2.2)
let (a,b,c) = origin; // destructuring in tuple
let firstElement = origin.0; // indexing

Enter fullscreen mode Exit fullscreen mode

Arrays

Arrays, in contrast to tuples, require each of their elements to share the same type. Unlike arrays in JS/TS, Rust arrays have a fixed length, making it impossible to add or remove elements directly. If dynamic resizing is needed, similar to arrays in JS/TS, Vectors in Rust would likely be the suitable alternative.

let origin: [i8, 3] = [1, 2, 3];
let origin = [4; 3] // means [4,4,4]
let first = origin[0];

Enter fullscreen mode Exit fullscreen mode

Variables and Mutability

In Rust, the default behavior for variables is immutability, meaning that their values cannot be changed once assigned. The letkeyword is used to declare variables, and if you want a mutable variable, you need to explicitly use mut after let.

// Immutable variable
let x = 5;
// Mutable variable
let mut y = 10;
y = 15; // Valid because y is mutable

Enter fullscreen mode Exit fullscreen mode

There are also constants too and one notable distinction between let is that you cannot use mut with constants. Constants, unlike variables, are inherently immutable. They are declared using the const keyword, and their type must be explicitly annotated.

const MULTIPLIER: u32 = 5;

Enter fullscreen mode Exit fullscreen mode

Constants can be declared in any scope, including the global scope, making them valuable for values shared across different parts of the code.

Functions

In the Rust programming language, function bodies consist of a series of statements and optionally end in an expression. Statements are instructions that perform actions but do not return a value, while expressions evaluate to a resultant value.

fn main() {
    let y = {
        let x = 3; // statement
        x + 1 // expression which evaluates the value assigned to y
    };

    println!("The value of y is: {y}");
}

Enter fullscreen mode Exit fullscreen mode

In JavaScript, you can create functions using either function declarations or expressions. In Rust, you can use function declarations or lambda functions, known as closures, each with its own syntax and distinctive features.

JS

// Function Declaration
function add(a, b) {
  return a + b;
}

// Function Expression
const subtract = function(a, b) {
  return a - b;
};

Rust

// Function Declaration
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// Closure (Lambda Function)
let subtract = |a: i32, b: i32| -> i32 {
    a - b
};

Enter fullscreen mode Exit fullscreen mode

Ownership, References, and Borrowing

Ownership is a fundamental concept in the Rust programming language that establishes a set of rules governing how the language manages memory throughout the program’s execution. In JavaScript, memory management is typically handled by a garbage collector, which automatically reclaims memory that is no longer in use, relieving developers of explicit memory management responsibilities. Unlike Rust, JavaScript’s abstraction of memory details makes it well-suited for high-level development but sacrifices fine-grained control over memory allocation and deallocation.

The stack, organized as a first-in, first-out (FIFO) fixed-size structure, contrasts with the heap, an unbounded and less organized memory space with an unknown size at compile time. Rust’s memory allocator dynamically locates an available space in the heap, designates it as in use, and returns a pointer representing the address of that location.

Key ownership rules in Rust include that each value can have only one owner, the value is dropped from memory when the owner goes out of scope, and there can be only one owner at any given time.

In Rust, the automatic memory management is facilitated by the drop function, which is called when a variable goes out of scope.

rust
{
 // `s` is not valid here; it’s not yet declared
 let s = "hello"; // `s` is valid from this point forward

 // do stuff with `s`
} // this scope is now over, and `s` is no longer valid
                 // this scope is now over, and s is no longer valid

Enter fullscreen mode Exit fullscreen mode

Rust uses references, denoted by the & symbol, allowing you to refer to a value without taking ownership of it. References ensure that the data they point to remains valid for the duration of the reference’s lifetime.

let original = String::from("hello");
let reference = &original; // 

Enter fullscreen mode Exit fullscreen mode

Rust’s references are immutable by default, meaning they cannot modify the data they point to. However, mutable references, denoted by &mut, allow modifications to the referenced data.

let mut value = 42;
let reference = &mut value; // Mutable reference to `value`

// Modify `value` through the mutable reference
*reference = 10;

Enter fullscreen mode Exit fullscreen mode

A significant restriction on mutable references is that if you have one, you cannot have any other references—mutable or immutable—to the same value.

let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // ERROR: Cannot have multiple mutable references to `s`

Enter fullscreen mode Exit fullscreen mode

This restriction prevents data races at compile time. Data races occur when multiple pointers access the same data concurrently, at least one of them modifies the data, and there’s no synchronization mechanism.

// In Rust, you cannot mix mutable and immutable references.
let s = String::from("hello");
let r1 = &s; // No problem
let r2 = &s; // No problem
let r3 = &mut s; // BIG PROBLEM: Cannot have a mutable reference alongside immutable references

Enter fullscreen mode Exit fullscreen mode

The Rust programming language enforces the rule that you can either have one mutable reference or any number of immutable references to a value—this is the principle of exclusive mutable or shared immutable references.

CTA Software

💖 💪 🙅 🚩
apium_hub
Apiumhub

Posted on January 22, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Dependency Pruning in Software Development
agilewebandappdevelo Dependency Pruning in Software Development

March 7, 2024

Rust Programming Language: A Beginner’s Guide
agilewebandappdevelo Rust Programming Language: A Beginner’s Guide

January 22, 2024

Going Native: Trying Out AOT For Spring Boot
agilewebandappdevelo Going Native: Trying Out AOT For Spring Boot

April 13, 2023

Web Components: Everything You Need to Know
agilewebandappdevelo Web Components: Everything You Need to Know

February 23, 2023