Rust: Mutability and Ownership (borrow checker)

Let’s talk about the mutability and ownership concepts in Rust programming language.

Mutability

If we want to re-assign the ripped_date int value, we can’t simply re-assign.

fn main() {
    let orange: String = String::from("Sweet Orange");
    let ripened_date: i8 = 1;
    >> ripened_date = 2;  // <== can't be re-assign
    println!("The orange has been ripened in {} date", ripened_date);
}

We should make ripened_date a mutable variable so it can be mutate.
Add mut in front of the variable name.

We should:

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;  // <<== we add 'mut' in front of the variable name
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);
}

Then we can mutate the ripened_date variable.


Ownership

Now, Let’s say we are shopkeepers to selling orange. We are now making the label to stick on the orange.
We add an new function called generate_label_for_selling

use std::fmt::format;

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), orange);
    println!("Current Label: {}", label);
}

fn generate_label_for_selling(ripened_date: String, orange: String) -> String {
    format!("ripened {} date, {}", ripened_date, orange)
}

This function accept a ripened date as String and the orange String.
The print result would be

The orange has been ripened in 2 date
Current Label: ripened 2 date, Sweet Orange

OK, now we add some code to print the result:

use std::fmt::format;

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), orange);
    println!("Current Label: {}", label);
    println!();
    println!("Product Name: {},\nProduct Label: {}", orange, label);  // <== print the final result
}

fn generate_label_for_selling(ripened_date: String, orange: String) -> String {
    format!("ripened {} date, {}", ripened_date, orange)
}

However, when we run the code, it won’t compile. It said borrow of moved value: orange.
How can we solve this problem? This is the concept of ownership comes in.

For example, we see the main function, println! macro and the label generator as three people.

The main function has an orange. and he/she pass this orange to the label generator to make the sell info label for the orange.

Then we get the label from the label generator.

After received the result, we call println! macro to print the final result. However, since the ownership of the orange still holds in label generator. The main function can’t give the macro any orange to print. (So it can’t compile)

How can we solve this problem?
We can send a reference into the label generator. The main function still an owner of an orange, but send a ticket to the label generator. This ticket let the generator able to access the orange. We can suppose the main function borrow the orange to the generator function.

The label generator now able to access the orange through this ticket. The label can be generated. The main function will get the label. And don’t forget he/she also owned the orange. So he/she can pass these two items to the println! macro with no issues.

use std::fmt::format;

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), &orange);
    println!("Current Label: {}", label);
    println!();
    println!("Product Name: {},\nProduct Label: {}", orange, label);
}

fn generate_label_for_selling(ripened_date: String, orange: &String) -> String {
    return format!("ripened {} date, {}", ripened_date, orange);
}

Now everything works fine.
The result will print:

Product Name: Sweet Orange,
Product Label: ripened 2 date, Sweet Orange

Mutable Reference

Let’s say when the label generator makes the new label, he/she should also add certificated words in the end of the product name. Just like this: Sweet Orange (certificated)

So we use dereference to modify a String:

use std::fmt::format;

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), &orange);
    println!("Current Label: {}", label);
    println!();
    println!("Product Name: {},\nProduct Label: {}", orange, label);
}

fn generate_label_for_selling(ripened_date: String, orange: &String) -> String {
    *orange += "(certificated)";  // <== use dereference to modify this String
    return format!("ripened {} date, {}", ripened_date, orange);
}

But the compiler start complaining.

cannot borrow `*orange` as mutable, as it is behind a `&` reference

As we can see, the reference is immutable by default. Just like the variable itself.
The label generator should borrow the mutable ticket from the main function. He/She will able to modify the String.
So put mut next to & , it will like &mut which means a mutable reference.

use std::fmt::format;

fn main() {
    let orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), &mut orange);  // <= to a mut refs
    println!("Current Label: {}", label);
    println!();
    println!("Product Name: {},\nProduct Label: {}", orange, label);
}

fn generate_label_for_selling(ripened_date: String, orange: &mut String) -> String { // <= to a mut refs
    *orange += "(certificated)";
    return format!("ripened {} date, {}", ripened_date, orange);
}

We still get an error message, which said:

cannot borrow `orange` as mutable, as it is not declared as mutable

That is because the orange variable itself is immutable. Let’s change to mutable.
Add mut to the orange variable.

use std::fmt::format;

fn main() {
    let mut orange: String = String::from("Sweet Orange");
    let mut ripened_date: i8 = 1;
    ripened_date = 2;
    println!("The orange has been ripened in {} date", ripened_date);

    let label = generate_label_for_selling(ripened_date.to_string(), &mut orange);
    println!("Current Label: {}", label);
    println!();
    println!("Product Name: {},\nProduct Label: {}", orange, label);
}

fn generate_label_for_selling(ripened_date: String, orange: &mut String) -> String {
    *orange += "(certificated)";
    return format!("ripened {} date, {}", ripened_date, orange);
}

Now everything will works.
The final println will print:

Product Name: Sweet Orange(certificated),
Product Label: ripened 2 date, Sweet Orange(certificated)

Happy Coding!

Leave a reply