:#/ GSLF Journal
← :#/ GSLF Journal

Rustlers Atom 2.3: Mutable and immutable borrow

On this page

<figure class="video" style="margin:0;">
<div style="position:relative;width:100%;padding-bottom:56.25%;height:0;overflow:hidden;">
<iframe src="https://www.youtube.com/embed/14ZB8dgd08?si=JuoLg594n1nlAuL"
title="YouTube video player"
style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen></iframe>
</div>
</figure>

Owning values is great, but moving them around constantly is stupid. Often, you want to let a function use a value without taking responsibility for freeing it. In Rust, this is called borrowing. You borrow a value by creating a reference. References are pointers that are guaranteed to be valid and align with Rust’s safety rules. There are two types.

Immutable Reference &T#

An immutable reference allows you to read data but not change it. You create one by adding & before the variable.

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // Pass a reference
    println!("The length of '{}' is {}.", s1, len); // s1 is still valid here
}

fn calculate_length(s: &String) -> usize {
    s.len()
} // s goes out of scope, but nothing happens because s didn't own the data

You can have as many immutable references to a value as you like, simultaneously. It’s like a read-only Google Doc: everyone can look, but nobody can type.

Mutable Reference &mut T#

If you want a function to modify the borrowed value, you use a mutable reference. You create it with &mut. To create a &mut reference, the owner variable must also be declared as mut.

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s); // "hello, world"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Dereferencing#

Under the hood, references are pointers. To access the data they point to, you technically need to dereference them using *. However, Rust has a feature called auto-dereferencing for method calls and field access.

let x = 5;
let y = &mut x;

*y += 1; // Explicit dereference needed to change the integer value

For most dot-methods (s.len(), v.push()), Rust handles the pointer logic for you automatically.