Rust’s Borrowing and Ownership

Rust developer’s guide from a Node.js perspective with Examples

--

After embarking on a thrilling journey with Rust in our previous article, we’re back to dive deeper into two core concepts that set Rust apart: borrowing and ownership. You can find the latest article here:

As a Node.js developer, you might be familiar with JavaScript’s garbage collector and the occasional performance issues it can cause. In this article, I’ll explore Rust’s unique approach to managing memory and preventing data races, with comparisons to Node.js and examples to help you grasp these concepts more easily.

Rust’s Ownership Model

In Rust, memory management is built around the idea of ownership. This ownership model helps guarantee memory safety without the need for a garbage collector. It is based on three main rules:

  1. Each value in Rust has a single owner.
  2. A value can be borrowed, but only one mutable borrow or multiple immutable borrows can exist simultaneously.
  3. When the owner goes out of scope, the value is dropped, freeing up the memory.

Let’s compare this to JavaScript. In Node.js, garbage collection handles memory management, meaning you don’t have to worry about manual memory allocation or deallocation. However, this can lead to performance issues and occasional memory leaks.

Ownership Example

Let’s look at a simple example of ownership in Rust and compare it to how it would work in Node.js:

fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}
// It will produce an error in compiling the code

In this Rust code snippet, we create a new String called s1. Then, we assign its value to s2. However, this code will not compile due to Rust's ownership rules. When we assign s1 to s2, ownership is transferred, making s1 invalid. Trying to use s1 afterwards results in a compilation error.

In Node.js, this code would work without any issues:

let s1 = "hello";
let s2 = s1;
console.log(`${s1}, world!`);

JavaScript’s garbage collector takes care of memory management, allowing you to use s1 even after assigning its value to s2.

Borrowing in Rust

To prevent the previous code not compiling, borrowing is the fundamental concept in Rust that allows you to temporarily access a value without taking ownership. There are two types of borrows: mutable and immutable. Let’s take a closer look.

Immutable Borrow

Here’s an example of an immutable borrow in Rust:

fn main() {
let s1 = String::from("hello");
let s2 = &s1;
println!("{}, world!", s1);
}

In this example, we create an immutable reference to s1 and assign it to s2. This allows us to use s1 later because we never transferred ownership.

Mutable Borrow

Now let’s see an example of a mutable borrow:

fn main() {
let mut s1 = String::from("hello");
let s2 = &mut s1;
s2.push_str(", world!");
println!("{}", s1);
}

In this case, we create a mutable reference to s1 and assign it to s2. We then modify the String through s2. However, this code will not compile. When we attempt to print s1, Rust enforces its borrowing rules and prevents us from using it while it's mutably borrowed.

In JavaScript, borrowing doesn’t exist as a concept because the garbage collector handles memory management. Here’s a similar example in Node.js:

let s1 = "hello";
let s2 = s1;
s2 += ", world!";
console.log(s1);

In this JavaScript example, we can see that the original s1 value remains unchanged even after modifying s2. This is because JavaScript, unlike Rust, doesn't have strict rules around borrowing and ownership.

Conclusion

In this article, we’ve taken a closer look at Rust’s borrowing and ownership concepts from a Node.js developer’s perspective. Rust’s strict rules around ownership and borrowing help ensure memory safety and prevent data races. While this may seem more complex compared to JavaScript’s garbage collector, Rust’s approach can lead to more efficient and reliable code in the long run.

By understanding these core Rust concepts, you’ll be better equipped to tackle Rust projects and appreciate the language’s unique approach to memory management. As a Node.js developer, you may find Rust’s paradigms challenging at first, but with time and practice, you’ll likely come to appreciate the safety and performance benefits they provide.

--

--

Giuseppe Albrizio
Rustified: JavaScript Developers’ Odyssey

Graduated in sound engineering and working as full-stack developer. I’ve spent these years in writing tons of line of codes and learning new things every day.