References in Rust allow us to point to a resource (value) without owning it. This means that the original owner of the resource remains the same.
References are helpful when passing values to a function that we do not want to change the ownership of. Creating a reference is known as borrowing in Rust.
Understanding References in Rust
Let's look at an example to learn about references in Rust.
fn main() {
let str = String::from("Hello, World!");
// Call function with reference String value
let len = calculate_length(&str);
println!("The length of '{}' is {}.", str, len);
}
// Function to calculate length of a string
// It takes a reference of a String as an argument
fn calculate_length(s: &String) -> usize {
s.len()
}
Output
The length of 'Hello, World!' is 13.
In the above example, we define a function called calculate_length()
which takes a &String
type as an argument.
The important part here is that s
is a reference to a String
and it doesn't take ownership of the actual value of String
.
fn calculate_length(s: &String) -> usize { // s is a reference to a String
s.len()
}
When, s
goes out of scope, at the end of the function, it is not dropped because it does not have ownership of what it refers to.
The function call looks like:
let str = String::from("Hello, World!");
let len = calculate_length(&str);
The &str
syntax while calling the function lets us create a reference that refers to the value of str
but does not own it.
The action of creating a reference is known as borrowing. Borrowing is when we borrow something, and we are done with it, we give it back. It doesn't make us the owner of the data.
Note: Ampersand (&) represents references, and they allow us to refer to some value without taking ownership of it.
Modifying a Reference in Rust
By default a reference is always immutable. However, we can use the &mut
keyword to make a reference mutable.
For example,
fn main() {
let mut str = String::from("Hello");
// before modifying the string
println!("Before: str = {}", str);
// pass a mutable string when calling the function
change(&mut str);
// after modifying the string
println!("After: str = {}", str);
}
fn change(s: &mut String) {
// push a string to the mutable reference variable
s.push_str(", World!");
}
Output
Before: str = Hello After: str = Hello, World!
Here, we set the variable str
to be mutable. Then we create a mutable reference with &mut str
, and call the change()
function with a mutable reference s: &mut String
.
This allows the change()
function to modify the value it borrows. Inside the change()
function, we push a string with s.push_str(", World!")
to the reference string.
Note: If you have a mutable reference to a value, you can have no other references to that value.
fn main() {
let mut str = String::from("hello");
// mutable reference 1
let ref1 = &mut str;
// mutable reference 2
let ref2 = &mut str;
println!("{}, {}", ref1, ref2);
}
Output
error[E0499]: cannot borrow `str` as mutable more than once at a time --> src/main.rs:8:16 | 5 | let ref1 = &mut str; | -------- first mutable borrow occurs here ... 8 | let ref2 = &mut str; | ^^^^^^^^ second mutable borrow occurs here 9 | 10 | println!("{}, {}", ref1, ref2); | ---- first borrow later used here
Rules of References
Rust primarily follows these rules of references at any given time:
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.