Rust for Python Developers: Ownership and Borrowing

credit: https://www.reddit.com/r/rustjerk/comments/9kjw8s/fearless_rust_parallelism/

Quick Glance at Fundamentals:

Good Old Stack and Heap:

In languages like Python, you never have to think about stack frames and heap. But when it comes to low-level languages, you need to have some basic understanding of how stack and heap work in the context of program execution. Even in a relatively high-level language like Go, these concepts are made explicit.

Stack vs Heap

Memory Management:

Now since the objects created on heap exists independently of run time stack, we have to clear those objects once we are done with them. So here is where two broad categories of languages diverge. One category uses manual memory management and the other one uses automatic memory management. In languages with automatic memory management, something called garbage collection is used to clear heap objects which are not being used anymore. Garbage collection keeps track of all the references made to a particular heap object. It runs at fixed time intervals, stopping the program execution and clearing the unused heap objects. This makes the program execution slightly non-deterministic in nature since it is generally difficult to judge when will garbage collection gets activated. To give an example, Python is a complete object-oriented language. Every single data you can create in Python gets stored in heap. Only the address to the objects gets stored on stack frame in Python. It uses a combination of reference counting and tracing garbage collector to clear heap objects. I don’t want to get into these terms to stay with the subject of this article.

Ownership:

Let’s look at this innocent looking code:

Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `a`
--> src/main.rs:8:49
|
7 | print_func(a);
| - value moved here
8 | println!("print inside main function {:?}", a);
| ^ value borrowed here after move
|
= note: move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.
  • Every data in Rust has a single owner which is its variable.
  • Data lives as long as its owner lives.
  • Data gets deleted precisely when the owner goes out of scope i.e., curly braces {}.
  • There can only be one owner at a time for the particular data.
Stack and Heap at the end of c = b
Stack and Heap Diagram
  • C++ way — copy the string. This way the entire string gets copied and sent to function, so the variable “a” still owns the original string. Clone() method can be used to create copy of the data.
  • Transferring ownership. Unique to Rust. Here “name” transfers the ownership to “a” again. I have added mut keyword here. In Rust, all variables are immutable by default. Since we are re-assigning to “a” we need to make it mutable.
  • Using reference. It is similar to C++ reference but with a significant difference which we will discuss shortly.

Borrowing:

It is tedious to transfer ownership from place to place. In addition, many functions might need to access the same data. The proper way to handle this is using references. In Python, ref counting solves this problem and handles freeing data correctly. In C/C++, when you create pointers to data, it’s upto you to make sure you don’t use the pointer after you free the object. Rust enforces this practice in compile time itself.

Compiling playground v0.0.1 (/playground)
error[E0597]: `v` does not live long enough
--> src/main.rs:6:13
|
6 | r = &v[2];
| ^ borrowed value does not live long enough
7 | }
| - `v` dropped here while still borrowed
8 | println!("{}", r);
| - borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.
  • You can have as many shared references as you want, adhering to the above lifetime constraints we saw earlier. But you cannot modify the original data through this. It means, you can have as many readers as you want.
  • You can have only one mutable reference to data at a time. When you borrow the data mutably, you can’t have any other kind of borrows which are both shared and mutable. In addition, you cannot borrow mutably from the shared reference either. It means, you can have only one writer at a time and in the mean time you cannot have any readers from the owner either. But you can have multiple shared references from the mutable reference.
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:13:16
|
11 | let r = &mut v;
| ------ mutable borrow occurs here
12 | add_to_vec(r);
13 | display_vec(&v);
| ^^ immutable borrow occurs here
14 | println!("{:?}",r);
| - mutable borrow later used here
error: aborting due to previous errorFor more information about this error, try `rustc --explain E0502`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Raja Sekar

Raja Sekar

Deep Learning practitioner, Distributed Systems enthusiast and a newbie entrepreneur