References
- References are pointers that must always be initialized.
- Taking a reference is known as borrowing.
fn main() {
let x1: Box<i32> = Box::new(5);
let r1: &Box<i32> = &x1;
}
-
The reference
r1
borrowsx1
.
References (2)
- Dereferencing a pointer accesses its data.
-
Box
is a smart pointer, which acts like a pointer but have additional metadata and capabilities.
fn main() {
let x1: Box<i32> = Box::new(5);
let n1: i32 = *x1;
let r1: &Box<i32> = &x1;
let n2: i32 = **r1;
let r2: &i32 = &*x1;
}
References (3)
References do not own the value they point to.
fn main() {
let s = String::from("crab");
display(&s);
println!("(2) {}", s);
} // `s` is freed here
fn display(name: &String) {
println!("(1) {name}");
}
References (4)
References can be mutable. This requires the referent to also be mutable.
fn main() {
let mut s1 = "prawn";
let r1 = &mut s1;
*r1 = "crab";
println!("{s1}");
}
This prints crab
.
Mutable Mutable References
Note the difference between a mutable reference and a mutable variable:
fn main() {
let mut s1 = "prawn";
let mut s2 = "words";
let mut r1 = &mut s1;
*r1 = "crab";
r1 = &mut s2;
*r1 = "language";
println!("{s1} {s2}");
}
This prints crab language
.
Mutable References and Functions
fn main() {
let mut x1 = 5;
increase(&mut x1);
println!("{x1}");
}
fn increase(r: &mut i32) {
*r += 1;
}
This prints 6
.
References and Aliasing
At any point in time, you can have either:
- One mutable reference
- Any number of immutable references
fn main() {
let x1 = 5;
{
let r1 = &mut x1;
}
{
let r1 = &x1;
let r2 = &x1;
let r3 = &x1;
let r4 = &x1;
}
}
References and Aliasing (2)
Variables cannot be mutated while they are borrowed.
fn main() {
let mut x1 = 5;
// `x1` is borrowed here
let r1 = &x1;
// `x1` is mutated here
x1 = 100;
// The borrow of `x1` is later used here
println!("{} {}", x1, *r1);
}
References and Aliasing (3)
Variables cannot be mutated while they are borrowed.
fn main() {
let mut x1 = 5;
// `x1` is borrowed here
let r1 = &x1;
// `x1` is mutated here
x1 = 100;
println!("{}", x1);
}
This compiles because the Rust compiler determined that r1
(and by extension, the borrow of x1
) is not used after the line x1 = 100
.
Dangling References
References must always be valid.
fn main() {
let animal: &String = get_crab();
println!("{}", animal);
}
fn get_crab() -> &String {
let s = String::from("crab");
&s
}
-
The variable
s
owns the string"crab"
. -
The variable
s
goes out of scope whenget_crab
returns. -
The memory for
"crab"
is deallocated whenget_crab
returns. -
If compiled, the variable
animal
will point to deallocated memory.