DEV Community

Cover image for Understand Ownership and borrowing in Rust
Dsysd Dev
Dsysd Dev

Posted on

Understand Ownership and borrowing in Rust

There are many tutorials available on this topic, here I try to show you how I understand this simple but very important concept

fn main() {
    let mut num = 42;

    // Immutable Borrow
    let borrowed_immutable = #
    println!("Immutable Borrow: {}", borrowed_immutable);

    // Mutable Borrow
    let borrowed_mutable = &mut num;
    *borrowed_mutable += 10;
    println!("Mutable Borrow: {}", borrowed_mutable);

    // Cannot do an immutable borrow while there's a mutable borrow
    // println!("Immutable Borrow: {}", borrowed_immutable);
}
Enter fullscreen mode Exit fullscreen mode

The main function starts by creating a mutable variable num with a value of 42.

Immutable Borrowing:

An immutable borrow is created with let borrowed_immutable = #. This means we're borrowing num in an immutable way, so we can read its value but not modify it.

We can have multiple such shared refereces.

Mutable Borrowing:

A mutable borrow is created with let borrowed_mutable = &mut num;. This allows us to modify the value of num through the borrowed reference.

Since it's a mutable borrow, we use the * operator to dereference the borrowed reference and modify the value. We add 10 to it.

The modified borrowed value is printed with println!.

Trying to create an Immutable Borrow again

The code that attempts to create an immutable borrow after a mutable borrow is commented out.

Rust does not allow an immutable borrow while there's a mutable borrow. This is to prevent potential data races.

In Rust, mutable and immutable borrows have different rules to ensure memory safety.

When you have a mutable borrow, no other borrow (mutable or immutable) can be active at the same time.

This prevents data races where multiple parts of your code try to change data concurrently, which can lead to unpredictable behavior.

Understanding Ownership

fn main() {
    let num: Box<i32> = Box::new(33);

    ownership(num);

    // println!("after owner: {}", num);
}

fn ownership(num: Box<i32>) {
    println!("owned: {}", num);

    let num2 = *num.as_ref() * 2;

    println!("new num: {}", num2);
}
Enter fullscreen mode Exit fullscreen mode

In the main function:

A variable named num is created and assigned a Box that contains the value 33.

The Box is a smart pointer created on heap that helps manage memory allocation and deallocation.

The ownership function is called, passing the num Box to it.

There's a commented-out line that attempts to print the value of num after it's been passed to the ownership function. 

However, this line is commented out because Rust's ownership system prevents using num after it's been moved to the ownership function.

The ownership function receives the ownership of the num Box.

The value inside the Box is printed using println!.

The value is then multiplied by 2 and assigned to the variable num2.

The new value, num2, is printed using println!.

The Key Concept

The key concept here is Rust's ownership system, which ensures memory safety and prevents certain types of bugs like null pointer dereferencing and data races.

In Rust, when a value is passed into a function (or moved to another variable), the ownership of that value is transferred.

This means the original variable can't be used afterward, to prevent issues like double freeing memory or using invalid references.

The Box type is used here to allocate memory on the heap for the i32 value (instead of the stack).

When the Box is passed to the ownership function, ownership of the data moves to the function.

This concept is called "moving" in Rust.

This is the rust default as compared to CPP where values are copied.

Video Format

If you prefer video format, then have a look at this short

https://www.youtube.com/shorts/AbsYKCEd-XU

Conclusion

This code demonstrates Rust's strict ownership model and how it helps prevent memory-related bugs.

It might look a bit complicated at first, but Rust's ownership and borrowing rules play a significant role in ensuring the safety and reliability of your programs.

Claps Please!

If you found this article helpful I would appreciate some claps 👏👏👏👏, it motivates me to write more such useful articles in the future.

Follow for regular awesome content and insights.

Subscribe to my Youtube channel

Subscribe to my youtube channel if you are on the lookout for more such awesome content in video format.

https://www.youtube.com/@dsysd-dev

Follow me on twitter 🐥

Join me on Twitter for a daily dose of knowledge, fascinating trivia, and valuable insights. Let's embark on a journey of continuous learning and discovery together! Follow me to stay inspired and informed. 🚀

https://twitter.com/dsysd_dev

Subscribe to my Newsletter

If you like my content, then consider subscribing to my free newsletter, to get exclusive, educational, technical, interesting and career related content directly delivered to your inbox

https://dsysd.beehiiv.com/subscribe

Important Links

Thanks for reading the post, be sure to follow the links below for even more awesome content in the future.
Twitter: https://twitter.com/dsysd_dev
Youtube: https://www.youtube.com/@dsysd-dev
Github: https://github.com/dsysd-dev
Medium: https://medium.com/@dsysd-dev
Email: dsysd.mail@gmail.com
Telegram 📚: https://t.me/dsysd_dev_channel
Linkedin: https://www.linkedin.com/in/dsysd-dev/
Newsletter: https://dsysd.beehiiv.com/subscribe
Gumroad: https://dsysd.gumroad.com/
Dev.to: https://dev.to/dsysd_dev/

Top comments (0)