Its time to introduce The Borrow Checker
The meme's are real, but the borrow checker isn't that hard if you have the right foundation.
- we have already talked about every thing is on the
stack
withptr
to theheap
if needed
(quick example)
Types of values
(code time)
value
reference
mutable reference
Terminology
Dropped - releasing memory
There are THREE rules you must have in your head at all times.
- There can only be one value owner
- There can be unlimited immutable borrows (reference) with no mutable references
- There can be only one mutable reference and no immutable references
There is one rule for Lifetimes
- A reference cannot outlive its value
Stated differently
One var owns the the data
One var can change the data
Many vars can look at the data
You cannot look and change the data simultaneously
You cannot refer to something that has been dropped (released in memory)
Test
- create a new struct
Item
, derivesDebug
, with one property,count
, that is anusize
. - create a new
fn
,add_one
that takes in anItem
and adds 1 to it. - In the main function, create
item
of typeItem
- Print out
item
(with Debug print) - pass
item
toadd_one
. - Print out
item
again.
Derives Debug
allows for printing of the struct.
#[derive(Debug)]
struct Item {...}
fn main() {
let item = Item { .. };
// --------v debug print
println!("{:?}", item);
// Item {
// count: 69
// }
}
First, what is causing the error?
What is causing the error?
#[derive(Debug)]
struct Item {
count: isize,
}
fn add_one(mut item: Item) {
item.count += 1;
}
fn main() {
let item = Item { count: 0 };
println!("item {:?}", item);
add_one(item);
println!("item {:?}", item); // <--- why does this error
}
Let me ask you this question, who owns Item
on this line?
let item = Item { count: 0 };
What about this line?
add_one(item);
Did you read the errors from your LSP?
Lets do a quick whiteboard memory layout
What rule are we breaking?
- There can only be one value owner
- There can be unlimited immutable borrows (reference) with no mutable references
- There can be only one mutable reference and no immutable references
How do we fix this?
(write code now)
Complete code
#[derive(Debug)]
struct Item {
count: isize,
}
fn add_one(item: &mut Item) {
item.count += 1;
}
fn main() {
let mut item = Item { count: 0 };
println!("item {:?}", item);
add_one(&mut item);
println!("item {:?}", item);
}
Lets make the borrow checker angry again!
To do this,
- create a function called
print_all
that takes in an immutable borrow (reference) toitems
and prints each item, one at a time
In the main function
- create a vector of
Item
s calleditems
- grab a mutable reference to item 0 (
get_mut
) - print item 0
- call
print_all
- print item 0 (you have to code this all)
So how did we break it?
Try to explain why this happened, then what rule did we break?
Complete Code
#[derive(Debug)]
struct Item {
count: isize,
}
fn add_one(item: &mut Item) {
item.count += 1;
}
fn print_all(items: &Vec<Item>) {
for item in items {
println!("print {:?}", item);
}
}
fn main() {
let mut items = vec![
Item { count: 0 },
Item { count: 0 },
Item { count: 0 },
];
let item = items.get_mut(0).unwrap();
add_one(item);
print_all(&items);
println!("help {:?}", item);
}
One more time
- get a mutable reference named
one
,get_mut(0)
- get a mutable reference named
two
,get_mut(1)
println!("{:?}", one)
What rule are we breaking?
Complete Code
#[derive(Debug)]
struct Item {
count: isize,
}
fn main() {
let mut items = vec![
Item { count: 0 },
Item { count: 0 },
Item { count: 0 },
];
let item = items.get_mut(0).unwrap();
let item2 = items.get_mut(1).unwrap();
println!("help {:?}", item);
}
does this error?
#[derive(Debug)]
struct Item {
count: isize,
}
fn main() {
let mut items = vec![
Item { count: 0 },
Item { count: 0 },
Item { count: 0 },
];
let item = items.get_mut(0).unwrap();
let item2 = items.get_mut(1).unwrap();
println!("help {:?}", item2);
}
Why or why not?
Now, with all of your knowledge why does this error?
fn main() {
let items = vec![1, 2, 3]
.iter()
.map(|x| x + 1);
println!("{:?}", items);
}
lets talk about why this happens!
Quick Recap: The big three rules
There can only be one value owner
let item = Item { age: 10 };
let other = item; // value moved here
println!("{:?}", item.age); // borrow of moved value (item moved to other)
There can be 0 mutable borrows when there are 1 or more immutable borrows
let mut items = vec![Item { age: 1 }, Item { age: 2 }];
let items2: &Vec<Item> = &items; // immutable borrow occurs here
let items3: &mut Vec<Item> = &mut items; // cannot borrow mutably with
// immutable references out
items2.get(0); // item3 is mutably borrowed
There can only be 1 mutable borrow
let mut items = vec![Item { age: 1 }, Item { age: 2 }];
let items2: &mut Vec<Item> = &mut items; // First mutable borrow
let items3: &mut Vec<Item> = &mut items; // Error occurs here
items2.push(Item { age: 3 }); // nope!
Applications of the rules
There is a "flow" to references
Since items2
was not used when items3
mutable borrow out, this is ok
let mut items = vec![Item { age: 1 }, Item { age: 2 }];
let items2: &mut Vec<Item> = &mut items; // First mutable borrow
items2.push(Item { age: 3 }); // ok!
let items3: &mut Vec<Item> = &mut items; // Second mutable borrow
items3.push(Item { age: 3 }); // still ok!
References cannot outlive their associated values
let y: &usize;
{
let x: usize = 5;
y = &x;
}
println!("ooh no! {}", y);
Test
Remember this code from the enum
section?
struct Custom {
name: String,
age: usize,
}
enum Item {
Number(usize),
Custom(Custom),
String(String),
}
fn main() {
let foo = Item::Number(5);
match &foo {
Item::Number(num) => println!("i am a number: {}", num),
Item::String(str) => println!("i am a string: {}", str),
Item::Custom(custom) =>
println!("name: {}, age: {}", custom.name, custom.age),
}
match &foo {
Item::Custom(custom) =>
println!("name: {}, age: {}", custom.name, custom.age),
_ => {}
}
// ...
}
What rule would we break if we changed the match statements from &foo
to
foo
?
Questions
I am sure you have a question or two... i can borrow