Reference

References are created explicitly with the & operator, and dereferenced explicitly with the * operator.


#![allow(unused)]
fn main() {
    let x = 10;
    let rx = &x;
    assert!(*rx == 10);

    let mut y = 11;
    let ry = &mut y;
    *ry += 1; 
    assert!(*ry == 12);
}

The . operator implicitly dereferences its left operand if needed.


#![allow(unused)]
fn main() {
pub struct Person {
    name : &'static str,
    age : f32
}

let john_ref = &john;

assert!(john_ref.name == "John"); // implicitly dereference
assert!((*john_ref).name == "John"); // same as above, but with explicit dereference
assert!(john_ref.age == 40.3);
assert!((*john_ref).age == 40.3);
}

The . operator can also implicitly borrow a reference to its left operand, if needed for a method call.


#![allow(unused)]
fn main() {
let mut v = vec![10, 20, 3];
v.sort(); // implicitly borrow a mutable reference to v
(&mut v).sort(); // same as above
}

Assigning References

Assigning to a Rust reference makes it point at a new value:


#![allow(unused)]
fn main() {
let x = 10;
let y = 20;
let mut r = &x;
println!("{r}");
r = &y;
println!("{r}");
}

References to References

Rust permits references to references.


#![allow(unused)]
fn main() {
struct Point { x : f32, y : f32 }

let p1 = Point { x : 1000, y : 729 };
let r : &Point = &p1;
let rr : &&Point = &r;
let rrr : &&&Point = &rr;

assert_eq!(rrr.y, p1.y);
assert_eq!((*(*(*rrr))).y, p1.y);
}

The . operator follows as many references as it takes to find its target as seen above rrr.y

Borrow an illustration from The Programming Rust Book

image

Comparing References

Like the . operator, Rust's comparison operators "see through" any number of references, as long as both operands have the same type.


#![allow(unused)]
fn main() {
let a = 10;
let b = 20;
let c = 10;

let ra = &a;
let rb = &b;
let rc = &c;

let rra = &ra;
let rrb = &rb;
let rrc = &rc;

assert!(rra < rrb);
assert!(rra <= rrb);
assert!(rra == rrc);
assert!(rrb > rrc);
assert!(rrb >= rrc);

// both operands should have same level of references
assert!(rrb >= &&10); 

// Arithmetic operators can see through only one level of references
assert!(rb == &(ra + &10));
}

Arithmetic operators can see through only one level of references

Checking references pointing to same memory location

std::ptr::eq


#![allow(unused)]
fn main() {
let x = 1;

let rx1 = &x;
let rx2 = &x;

println!("{}", std::ptr::eq(rx1, rx2));
}

References are never Null and there is no default initial value for a reference. If you need a value that is either a reference to something or not, use the type Option<&T>.

Borrowing references to arbitrary expressions

Rust allows you borrow a reference to the value of any sort of expression at all.

fn get_str() -> String {
    "hello".to_string()
}

fn main() {
    // Rust here create an anonymous variable 
    // and make sure it is variable as long as the variable s
    let s = &get_str(); 
    println!(s);
}