As long as there are shared references to a value, not even its owner can modify it; the value is locked down.
If there is a mutable reference to a value, it has exclusive access to the value; you can’t use the owner at all, until the mutable reference goes away.
Working with References
To create a mutable reference, use the &mut operator:
letmuty=32;letm=&muty;// &mut y is a mutable reference to y
The . operator implicitly dereferences its left operand, if needed:
structAnime{name:&'staticstr,bechdel_pass:bool};letaria=Anime{name:"Aria: The Animation",bechdel_pass:true};letanime_ref=&aria;assert_eq!(anime_ref.name,"Aria: The Animation");// Equivalent to the above, but with the dereference written out:assert_eq!((*anime_ref).name,"Aria: The Animation");letmutv=vec![1973,1968];v.sort();// implicitly borrows a mutable reference to v(&mutv).sort();// equivalent, but more verbose
In Rust you use the & and * operators to create and follow references, with the exception of the . operator, which borrows and dereferences implicitly.
References to References
Rust permits references to references:
The . operator follows as many references as it takes to find its target.
Like the . operator, Rust’s comparison operators “see through” any number of references.
References Are Never Null
In Rust, if you need a value that is either a reference to something or not, use the type Option<&T>.
Borrowing References to Arbitrary Expressions
In situations like this, Rust simply creates an anonymous variable to hold the expression’s value and makes the reference point to that.
References to Slices and Trait Objects
Rust also includes two kinds of fat pointers, two-word values carrying the address of some value, along with some further information necessary to put the value to use.
A reference to a slice carries the starting address of the slice and its length.
A reference to a value that implements a certain trait. A trait object carries a value’s address and a pointer to the trait’s implementation appropriate to that value, for invoking the trait’s methods.
Reference Safety
Borrowing a Local Variable
Rust tries to assign each reference type in your program a lifetime that meets the constraints imposed by how it is used.
At run time, a reference is nothing but an address; its lifetime is part of its type and has no run-time representation.
A variable’s lifetime must contain or enclose that of the reference borrowed from it.
Since v owns the vector, which owns its elements, the lifetime of v must enclose that of the reference type of &v[1].
Receiving References as Function Arguments
Rust’s equivalent of a global variable is called a static: it’s a value that’s created when the program starts and lasts until it terminates.Consider a function which assigns value to a static variable:
Bad Attempt: Since we must allow 'a to be any lifetime, things had better work out if it’s the smallest possible lifetime: one just enclosing the call to f. Then there is a mismatch between p and the STASH regarding lifetime.
Passing References to Functions
This fails to compile: the reference &x must not outlive x, but by passing it to f, we constrain it to live at least as long as 'static.
Returning References
Suppose we call smallest like this:
The lifetime of ¶bola and s should be the same as dictated by the funtion signature, which assigns the same lifetime parameter 'a.
Structs Containing References
Whenever a reference type appears inside another type’s definition, you must write out its lifetime.
Suppose we have a parsing function that takes a slice of bytes and returns a structure holding the results of the parse:
Without looking into the definition of the Record type at all, we can tell that, if we receive a Record from parse_record, whatever references it contains must point into the input buffer we passed in, and nowhere else (except perhaps at 'static values).
Distinct Lifetime Parameters
Omitting Lifetime Parameters
If there’s only a single lifetime that appears among your function’s parameters, then Rust assumes any lifetimes in your return value must be that one:
If your function is a method on some type and takes its self parameter by reference, then that breaks the tie: Rust assumes that self’s lifetime is the one to give everything in your return value.
Sharing Versus Mutation
We may borrow a mutable reference to the vector, and we may borrow a shared reference to its elements, but those two references’ lifetimes must not overlap.
struct Point { x: i32, y: i32 }
let point = Point { x: 1000, y: 729 }; let r: &Point = &point;
let rr: &&Point = &r;
let rrr: &&&Point = &rr;
fn factorial(n: usize) -> usize {
(1..n+1).product()
}
let r = &factorial(6);
assert_eq!(r + &1009, 1729);
let v = vec![1, 2, 3];
let r = &v[1];
static mut STASH: &i32 = &128;
// Bad Attempt
fn f(p: &i32) {
unsafe {
STASH = p;
}
}
// equals to `fn f<'a>(p: &'a i32)`
// the lifetime 'a (pronounced “tick A”) is a lifetime parameter of f. You can read <'a> as // “for any lifetime 'a”
// Good Attempt
fn f(p: &'static i32) {
unsafe {
STASH = p;
}
}
fn f(p: &'static i32) { ... }
let x = 10;
f(&x);
fn smallest(v: &[i32]) -> &i32 {
let mut s = &v[0];
for r in &v[1..] {
if *r < *s { s = r; }
}
s
}
// The signature is equivalent to fn smallest<'a>(v: &'a [i32]) -> &'a i32 { ... }
let s;
{
let parabola = [9, 4, 1, 0, 1, 4, 9];
s = smallest(¶bola);
}
assert_eq!(*s, 0); // bad: points to element of dropped array
struct S<'a> {
r: &'a i32
}
struct D<'a> {
s: S<'a>
}
// By taking a lifetime parameter 'a and using it in s’s type, we’ve allowed Rust to relate D value’s lifetime to that of the reference its S holds.
let mut x = 10;
let r1 = &x;
let r2 = &x; // ok: multiple shared borrows permitted
x += 10; // error: cannot assign to `x` because it is borrowed
let m = &mut x; // error: cannot borrow `x` as mutable because it is
// also borrowed as immutable
println!("{}, {}, {}", r1, r2, m); // the references are used here,
// so their lifetimes must last
// at least this long
let mut y = 20;
let m1 = &mut y;
let m2 = &mut y; // error: cannot borrow as mutable more than once
let z = y; // error: cannot use `y` because it was mutably borrowed
println!("{}, {}, {}", m1, m2, z); // references are used here
let mut w = (107, 109);
let r = &w;
let r0 = &r.0; // ok: reborrowing shared as shared
let m1 = &mut r.1; // error: can't reborrow shared as mutable
println!("{}", r0); // r0 gets used here
let mut v = (136, 139);
let m = &mut v;
let m0 = &mut m.0; // ok: reborrowing mutable from mutable
*m0 = 137;
let r1 = &m.1; // ok: reborrowing shared from mutable,
// and doesn't overlap with m0
v.1; // error: access through other paths still forbidden
println!("{}", r1); // r1 gets used here