let a = 10;
let b = a;
println!("{}", a); // 10
println!("{}", b); // 10
10
10
[E0382] Error: borrow of moved value: `a` ╭─[command_7:1:1] │ 1 │ let a = String::from("Hello"); │ ┬ │ ╰── move occurs because `a` has type `String`, which does not implement the `Copy` trait 2 │ let b = a; │ ┬│ │ ╰── value moved here │ │ │ ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()` 3 │ println!("{}", a); // Hello │ ┬ │ ╰── value borrowed here after move ───╯
Oops error! Why the first one is okay but the second one is not?
Two example above yield different results because int
implements the Copy
trait, while String
does not.
Copy
traits are copied when they are assigned to another variable.Copy
trait are moved when they are assigned to another variable.0x16b856810
0x16b856814
While in the case of String
, there is only one object in the memory (no implicit Copy), both a
and b
point to the same object.
Rust does NOT allow multiple owners of the same object. This is why the second example fails. (we will discuss this in more detail later)
To achieve the same result as the first example, we can use the clone
method to create a new object.
Hello
Hello
0x16b8567e0
0x16b8567f8
Copy
is a special trait that is used for types that can be copied by simply copying bits. This is mainly used for simple types like integers, floats, and booleans. If a type implements the Copy
trait, an older variable is still usable after assignment.
While Clone
trait is used for types that cannot be copied by simply copying bits. If a type implements the Clone
trait, we can create a new object by cloning the original object. This often involves allocating memory on the heap and deep copying the original object.
From now on, our focus will be on non-Copy
types.
Stack and heap are two different memory regions in a program.
Source: https://endjin.com/blog/2022/07/understanding-the-stack-and-heap-in-csharp-dotnet
Let’s see them in action
[E0382] Error: borrow of moved value: `x` ╭─[command_33:1:1] │ 1 │ let x = String::from("Hello"); │ ┬ │ ╰── move occurs because `x` has type `String`, which does not implement the `Copy` trait 2 │ let y = x; // the owner is transferred to y │ ┬│ │ ╰── value moved here │ │ │ ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()` │ 5 │ println!("{}", x); │ ┬ │ ╰── value borrowed here after move ───╯
[E0382] Error: borrow of moved value: `x` ╭─[command_35:1:1] │ 5 │ let x = String::from("Hello"); │ ┬ │ ╰── move occurs because `x` has type `String`, which does not implement the `Copy` trait 6 │ println!("{}", uppercase(x)); // the value of x is moved to the function parameter │ ┬│ │ ╰── value moved here │ │ │ ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()` │ 8 │ println!("{}", x); // error │ ┬ │ ╰── value borrowed here after move ───╯
Wow, so many errors! Let’s fix them one by one.
The easiest way to fix the errors is to clone the String
object. This way, we can create a new object on the heap and assign it to the new variable.
Hello
Hello
HELLO
Hello
Solved!
But wait, we are using more memory than necessary.
We can do better by using borrowing
. Borrowing allows us to pass a reference to the object instead of passing the object itself. This way, we can avoid creating a new object on the heap.
Hello
Hello
HELLO
Hello
HELLO
Hello
Inside the function call, s
owns the object.
After the function call, the ownership is transferred to x
By default, references are immutable. This means we cannot change the value of the object through the reference.
[E0596] Error: cannot borrow `*s` as mutable, as it is behind a `&` reference ╭─[command_25:1:1] │ 1 │ fn change_to_upper(s: &String) { │ │ │ ╰─ help: consider changing this to be a mutable reference: `mut ` 2 │ s.make_ascii_uppercase() │ ┬ │ ╰── `s` is a `&` reference, so the data it refers to cannot be borrowed as mutable │ │ Note: You can change an existing variable to mutable like: `let mut x = x;` ───╯
To be able to change the value of the object, we need to use a mutable reference. &mut
There are additional rules for references.
1. Only one mutable reference to an object is allowed at a time.
Hello
Hello
Hello
[E0499] Error: cannot borrow `x` as mutable more than once at a time ╭─[command_50:1:1] │ 3 │ let y = &mut x; │ ───┬── │ ╰──── first mutable borrow occurs here 4 │ let z = &mut x; │ ───┬── │ ╰──── second mutable borrow occurs here │ 7 │ println!("{}", y); // hello │ ┬ │ ╰── first borrow later used here ───╯
[E0502] Error: cannot borrow `x` as immutable because it is also borrowed as mutable ╭─[command_50:1:1] │ 3 │ let y = &mut x; │ ───┬── │ ╰──── mutable borrow occurs here │ 6 │ println!("{}", x); // hello │ ┬ │ ╰── immutable borrow occurs here 7 │ println!("{}", y); // hello │ ┬ │ ╰── mutable borrow later used here ───╯
Why?
To prevent race conditions. If we have multiple mutable references to an object and they run concurrently, they can change the object in an unpredictable way.
2. When an object has a mutable reference, it cannot be accessed by other references (both mutable & immutable).
[E0502] Error: cannot borrow `x` as immutable because it is also borrowed as mutable ╭─[command_36:1:1] │ 3 │ let y = &mut x; │ ───┬── │ ╰──── mutable borrow occurs here 4 │ let z = &x; // error │ ─┬ │ ╰── immutable borrow occurs here │ 7 │ println!("{}", y); │ ┬ │ ╰── mutable borrow later used here ───╯
[E0502] Error: cannot borrow `x` as immutable because it is also borrowed as mutable ╭─[command_36:1:1] │ 3 │ let y = &mut x; │ ───┬── │ ╰──── mutable borrow occurs here │ 6 │ println!("{}", x); │ ┬ │ ╰── immutable borrow occurs here 7 │ println!("{}", y); │ ┬ │ ╰── mutable borrow later used here ───╯
[E0382] Error: use of moved value: `person` ╭─[command_3:1:1] │ 7 │ fn greet(self) { │ ──┬─ │ ╰─── note: `Person::greet` takes ownership of the receiver `self`, which moves `person` │ 12 │ let person = Person { │ ───┬── │ ╰──── move occurs because `person` has type `Person`, which does not implement the `Copy` trait │ 16 │ person.greet(); │ ───┬─── │ ╰───── `person` moved due to this method call 17 │ person.greet(); //error │ ───┬── │ ╰──── value used here after move ────╯
To understand it better, actually
is equivalent to
[E0382] Error: use of moved value: `person` ╭─[command_45:1:1] │ 5 │ let person = Person { │ ───┬── │ ╰──── move occurs because `person` has type `Person`, which does not implement the `Copy` trait │ 9 │ greet(person); │ ───┬── │ ╰──── value moved here 10 │ greet(person); //error │ ───┬── │ ╰──── value used here after move ────╯
We have learned that in the case of function call, the function can take ownership of the object. This is why the above code does not work.
The fix is actually similar to the one we have seen before. We can use borrowing to pass a reference, i.e. &self
instead of self
.
Hello, my name is Alice
Hello, my name is Alice
The code above is equivalent to
The power of borrowing looks even more impressive when we talk about slices.
struct Person {
name: String,
age: u8,
}
fn first(v: &Vec<Person>) -> &Person {
&v[0]
}
fn test_slices() {
let mut people = vec![
Person {
name: String::from("Alice"),
age: 30,
},
Person {
name: String::from("Bob"),
age: 25,
}
];
let first_person = first(&people);
println!("{}", first_person.name);
people.remove(0);
}
test_slices();
Alice
Ok, it runs well. But do you notice the problem?
After remove(0)
, the first_person
doesn’t exist anymore. But we still have a reference to it. This is a dangling reference, which is a common problem in programming.
Before people.remove(0)
, this is what we have:
But after people.remove(0)
, the object is removed from the memory. first_person
is now a dangling reference.
Before seeing how Rust handles this problem, let’s see how Golang handles it.
Rewritten in Golang:
package main
import (
"fmt"
"slices"
)
type Person struct {
name string
age uint8
}
func first(v *[]Person) *Person {
return &(*v)[0]
}
func testSlices() {
people := []Person{
{
name: "Alice",
age: 30,
},
{
name: "Bob",
age: 25,
},
}
firstPerson := first(&people)
fmt.Println(firstPerson.name)
// remove the first element
_ = slices.Delete(people, 0, 1)
fmt.Println(firstPerson.name)
}
func main() {
testSlices()
}
What is the second output? Bob! Isn’t it expected to point to Alice?
Now, let’s see how Rust handles this problem.
struct Person {
name: String,
age: u8,
}
fn first(v: &Vec<Person>) -> &Person {
&v[0]
}
fn test_slices() {
let mut people = vec![
Person {
name: String::from("Alice"),
age: 30,
},
Person {
name: String::from("Bob"),
age: 25,
}
];
let first_person = first(&people);
println!("{}", first_person.name);
people.remove(0);
println!("{}", first_person.name);
}
test_slices();
[E0502] Error: cannot borrow `people` as mutable because it is also borrowed as immutable ╭─[command_5:1:1] │ 22 │ let first_person = first(&people); │ ───┬─── │ ╰───── immutable borrow occurs here │ 25 │ people.remove(0); │ ────────┬─────── │ ╰───────── mutable borrow occurs here │ 27 │ println!("{}", first_person.name); │ ────────┬──────── │ ╰────────── immutable borrow later used here ────╯
Rust compiler is smart enough to prevent dangling references. It will give us a compile-time error if we try to access a dangling reference.
But what if we want to access the first element after removing it? We can, just explicitly clone the object.
// Important: need to make the struct Person cloneable
#[derive(Clone)]
struct Person {
name: String,
age: u8,
}
fn first(v: &Vec<Person>) -> &Person {
&v[0]
}
fn test_slices() {
let mut people = vec![
Person {
name: String::from("Alice"),
age: 30,
},
Person {
name: String::from("Bob"),
age: 25,
}
];
let first_person = first(&people).clone(); // clone the value
println!("{}", first_person.name);
people.remove(0);
println!("{}", first_person.name); // this reference is still valid
}
test_slices();
Alice
Alice
Before people.remove(0)
:
After:
How about if we don’t want to get the reference to the first element, but we want to take ownership of it? i.e. we don’t want &Person
but Person
.
Let’s try
struct Person {
name: String,
age: u8,
}
fn first(v: &Vec<Person>) -> Person {
// return the array element, not the reference
v[0]
}
fn test_slices() {
let mut people = vec![
Person {
name: String::from("Alice"),
age: 30,
},
];
let first_person = first(&people);
println!("{}", first_person.name);
}
test_slices();
[E0507] Error: cannot move out of index of `Vec<Person>` ╭─[command_27:1:1] │ 7 │ v[0] │ ──┬─ │ ╰─── move occurs because value has type `Person`, which does not implement the `Copy` trait ───╯
Remember the ownership rules? When we return the object, the ownership is transferred to the caller.
Move can’t happen because the field is inside a Vec. And only one owner is allowed at a time.
Hmm, but is it a special behavior of a Vec? Let’s try replacing Vec
with our own “array”, People
:
struct Person {
name: String,
age: u8,
}
struct People {
person1: Person,
person2: Person
}
fn first(v: &People) -> Person {
v.person1
}
fn test_slices() {
let people = People {
person1: Person {
name: String::from("Alice"),
age: 30,
},
person2: Person {
name: String::from("Bob"),
age: 25,
},
};
let first_person = first(&people);
println!("{}", first_person.name);
}
test_slices();
[E0507] Error: cannot move out of `v.person1` which is behind a shared reference ╭─[command_37:1:1] │ 12 │ v.person1 │ ────┬──── │ ╰────── move occurs because `v.person1` has type `Person`, which does not implement the `Copy` trait ────╯
Wow, that also can’t be moved. Nice!
struct Person {
name: String,
age: u8,
}
fn first(p1: &Person, _p2: &Person) -> Person {
*p1
}
fn test_slices() {
let person1 = Person {
name: String::from("Alice"),
age: 30,
};
let person2 = Person {
name: String::from("Bob"),
age: 25,
};
let first_person = first(&person1, &person2);
println!("{}", first_person.name);
}
test_slices();
[E0507] Error: cannot move out of `*p1` which is behind a shared reference ╭─[command_24:1:1] │ 7 │ *p1 │ ─┬─ │ ╰─── move occurs because `*p1` has type `Person`, which does not implement the `Copy` trait ───╯
It turns out we can’t move the object because it is owned by someone else. We can’t move the object out of the Vec
because the Vec
owns the object, we can’t move the object out of the People
because the People
owns the object.
Similar to borrowing rule in a real life. We can’t move an object from one owner to another without the owner’s consent.
So, let’s now get that consent!
struct People {
person1: Person,
person2: Person
}
// pass the People object, taking ownership
fn first(v: People) -> (People, Person) {
// return back the ownership + the value
(v, v.person1)
}
fn test_slices() {
let people = People {
person1: Person {
name: String::from("Alice"),
age: 30,
},
person2: Person {
name: String::from("Bob"),
age: 25,
},
};
let (_people, first_person) = first(people);
println!("{}", first_person.name);
}
test_slices();
[E0382] Error: use of moved value: `v.person1` ╭─[command_28:1:1] │ 7 │ fn first(v: People) -> (People, Person) { │ ┬ │ ╰── move occurs because `v` has type `People`, which does not implement the `Copy` trait │ 9 │ (v, v.person1) │ ┬ ────┬──── │ ╰───────────── value moved here │ │ │ ╰────── value used here after move ───╯
Hmm…
std::mem::replace
std::mem::replace
is a function that takes ownership of the object and replaces it with a new object. It’s worth noting that std::mem::replace
is a safe function because it guarantees that the object will be replaced and not left dangling.
struct People {
person1: Person,
person2: Person
}
fn first(mut v: People) -> (People, Person) {
// replace the value of person1
let old_person1 = std::mem::replace(&mut v.person1, Person {
name: String::from("Charlie"),
age: 20,
});
(v, old_person1)
}
fn test_slices() {
// need to make it mutable
let mut people = People {
person1: Person {
name: String::from("Alice"),
age: 30,
},
person2: Person {
name: String::from("Bob"),
age: 25,
},
};
let (people, first_person) = first(people);
println!("{}", first_person.name);
println!("{}", people.person1.name);
}
test_slices();
Alice
Charlie
std::mem::take
std::mem::take
is quite similar to std::mem::replace
. The difference is that std::mem::take
replaces the object with the default value of the object.
// need to derive Default
#[derive(Default)]
struct Person {
name: String,
age: u8,
}
// also need to derive Default
#[derive(Default)]
struct People {
person1: Person,
person2: Person
}
fn first(mut v: People) -> (People, Person) {
// take the value of person1 and replace it with the default value
let old_person1 = std::mem::take(&mut v.person1);
(v, old_person1)
}
fn test_slices() {
// need to make it mutable
let mut people = People {
person1: Person {
name: String::from("Alice"),
age: 30,
},
person2: Person {
name: String::from("Bob"),
age: 25,
},
};
let (people, first_person) = first(people);
println!("{}", first_person.name);
println!("{}", people.person1.name); //empty
}
test_slices();
Alice
Let’s see how we can use std::mem::replace
to take ownership of the first element of a Vec
.
[1, 0, 3]
2
Ownership rules may also confuse us when we use loops.
[E0382] Error: use of moved value: `nums` ╭─[command_7:1:1] │ 2 │ let nums = vec![1, 2, 3, 4, 5]; │ ──┬─ │ ╰─── move occurs because `nums` has type `Vec<i32>`, which does not implement the `Copy` trait │ 4 │ for num in nums { │ ┬─┬─ │ ╰───── help: consider iterating over a slice of the `Vec<i32>`'s content to avoid moving into the `for` loop: `&` │ │ │ ╰─── `nums` moved due to this implicit call to `.into_iter()` │ 9 │ for num in nums { │ ──┬─ │ ╰─── value used here after move │ │ Note: note: `into_iter` takes ownership of the receiver `self`, which moves `nums` ───╯
Nah, tantrum again! What’s wrong?
When we call for num in nums
, it implicitly calls nums.into_iter()
. This method consumes the object and returns an iterator that takes ownership of the object.
Let’s take a look at .into_iter()
signature:
Since self
is passed by value, it consumes the object. This is why we can’t use nums
after the loop.
The alternative is to use iter()
which borrows the object.
Let’s try .iter()
1
2
3
4
5
1
2
3
4
5
Or we can just loop over &nums
:
1
2
3
4
5
1
2
3
4
5
.iter()
returns an immutable reference to the object. This means we can’t modify the object through the iterator.
[E0594] Error: cannot assign to `*num`, which is behind a `&` reference ╭─[command_21:1:1] │ 4 │ for num in &nums { │ ─┬┬── │ ╰───── help: use a mutable iterator instead: `mut ` │ │ │ ╰──── this iterator yields `&` references 5 │ *num += 1 │ ────┬──── │ ╰────── `num` is a `&` reference, so the data it refers to cannot be written ───╯
We can’t modify because it is borrowed immutably. Let’s try borrowing mutably.
[2, 3, 4, 5, 6]
Or explicitly .iter_mut()