Rust Ownership — Animated Intro

Mohammed El Batya
comsystoreply
Published in
5 min readAug 8, 2023

--

Ferris protecting your ownings.

The web is full of great content explaining how and why to use the move & borrow semantics in Rust. But it is still hard to build an intuition for it.

Although it’s important to struggle with abstract concepts to build the intuition, I would still like to ease that struggle a tiny bit for you. — At least for the visual learners among you.

Copy

A copy can only happen with types that implement the Copy trait. If you don’t know what a trait is, read this.

When a copyable value gets assigned to a new variable, it is simply copied.

#[derive(Copy)] // ipmlement the Copy trait
struct Circle; // define a type called Cirlcle

let purple = Circle; // create a Circle instance and assign it to a variable
let green = purple; // assign the value of `purple` to the variable `green`
A purple box appears, pointing to a blue circle. Then a green box appears, pointing to a copy of the blue circle.
let green = purple; // copies the Circle instance

Are you kidding!? … It’s ludicrous! … Do I really need to add Copy to all my types? … What about HUGE String instances? … I certainly don’t want to copy those around … Isn’t Rust supposed to be fast and memory efficient!?

Move

Each value must be “owned” by exactly one variable.

If the type value cannot be copied, the value gets moved instead. As a side effect, the initial variable will lose access to the value and become useless.

struct Circle; // define a type called Cirlcle, that does NOT implement Copy

let purple = Circle; // create a Circle instance and assign it to a variable.

let green = purple; // assign the value of `purple` to the variable `green`
// from a point on, the `purple` variable can
// no longer be used.
A purple box appears, pointing to a blue circle. Then a green box appears.  The blue circles moves to the green box. Now, only the green box points to the blue circle.
let green = purple; // moves the Circle instance

Accessing the purple variable after moving its value to green would cause a compile error, as shown in the rust book.

Are you saying that … if a value does not implement Copy … and I pass it to a function as an argument … the function will eat my value … and it is lost forever? … hmm … maybe I could rescue that poor value by using it as return value …. but what about the actual return value?! … that is so silly … Rust just makes no sense!

Borrow

Often, you would rather not copy or move a value, but still pass it along.

As a function argument, for example. After the function is done with your value, you want to continue using your variable and its value outside the function.

This is possible by creating a shared reference to the value, and passing that reference instead of the actual value along.

This is called borrowing in Rust.

You can create multiple shared references, also known as borrows, to the same value. That’s why they are “shared”.

There is a major disadvantage, though.
As long as a shared reference to a value exists, it cannot be mutated or moved.

As soon as all Borrows are gone, the initial owning variable gets the full control back.

struct Circle; 

let purple = Circle;

fn fancy_function(green: &Circle){
// do something with borrowed value until the function ends.
}

// the value of purple get's borrowed with & and assigned to the parameter
// of the fancy_function named "green".
fancy_function(&purple);
A purple box points to a blue circle. Then the Green box also points to the same blue circle. The connections change from black to grey. The green box then vanishes, and only the purple box points to the circle again. The remaining connection switches back from grey to black.
fancy_function(&purple); // borrows the Circle instance

Alright … alright! But what if I want to mutate my value in the function. Let’s say … I wish to change the diameter of the circle! … What should I do?

Mutable Borrow

In other cases, you want write-access to a value without copying or moving it. The shared borrow with ampersand (&) won’t help you here, since it will make the value read-only as long a borrow exists.

Arghh … This whole topic suddenly becomes existential! … What does it mean to exist? … Is living and existing the same? … Does the start and end of existence define someone's lifetime? … Lifetimes … damn … that’s another perplexing Rust concept I need to learn soon… enough! … I need to concentrate! … Back to mutable borrows!

For these cases, Rust offers you mutable borrows (&mut). Unlike with shared borrows, you are not allowed to have any additional borrow to that value. Even the originally owning variable is not allowed to read or mutate the value, as long as the mutable borrow exists.

struct Circle{diameter: u32};

let purple = Circle{diameter: 5};

fn fancy_function(green: &mut Circle){
green.diameter = 10;
// at the end of this function, "green" ceases to exist
}

// the value of purple get's borrowed with mut& and assigned to the parameter
// of the fancy_function named "green".
fancy_function(&mut purple);
A purple box points to a blue circle. Then the Green box also points to the same blue circle. The purple connection disappears. The green box then vanishes, and only the purple box points to the circle again. The connection between the purple box and the circle reappears.
fancy_function(mut& purple); // borrows the Circle instance mutably

but … but … what if … I really … really … really! … want multiple shared references to a value that allow mutability! … This crazy language is soooo … restrictive!

Smart Pointers

For most cases, copy, move, shared and mutable references are everything you need. They have been carefully designed and restricted so that a large amount of common programming mistakes are more or less impossible.

For cases where you require additional flexibility and capabilities for your references, Rust got some remarkable smart pointer types for you. They are safe to use, too.

Ok … I guess … I will go now, … and skim through the smart pointer documentation on my own. … Let’s see what other pointer types are out there, … just in case I need them in the future.

See u later alligator! … and sorry for the bad attitude.

Outro

Share the Animations

If you think that the animations in this article are useful, just copy and share them. I release them under the Creative Commons BY-SA 4.0 licence.

Feedback and contact

Please let me know if you would like me to write more articles like this about Rust. Positive or constructive feedback in the comments section is always welcome, and please leave a few claps if you liked it.

You can also find me on LinkedIn.

--

--