Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 5: References

Shared references are Copy

Mutable references are NOT Copy

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!

Iterating over a shared reference to a HashMap is defined to produce shared references to each entry's key and value.

IMPORTANT:

  • Since references are so widely used in Rust, the . operator implicitly dereferenecs its left operand, if need.
  • The . operator can also implicitly borrow a reference to its operand, if needed for a method call
#![allow(unused)]
fn main() {
let mut v = vec![1973, 1968];
v.sort();
(&mut v).sort();; // equivalent, but more verbos
}

Also, Rust permits reference to references. On the other hand, the . operator follows as many references as it takes to find its target:

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

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

assert_eq!(rrr.y, 1000);
}

If you actually want to know whether two references point to the same memory, you can use std:prt::eq which compares their addresses:

#![allow(unused)]
fn main() {
assert!(rx == ry);              // their referent are equivalent
assert!(!std::ptr::eq(rx, ry)); // but occupy different addresses
}

Rust references are nevel null. You can't use any variable until it's been initialized, regardless of its type Rust won't convert integers to references (outside of unsafe code), so you can't zero into a reference.

  • In Rust, if you need a value that is either a reference to something or not, use the type Option<&T>
  • At the machine level, Rust referents None as a null pointer and Some(r), where r is a &T value, as the nonzero address, so Option<&T> is just as efficient as a nullable pointer in C or C++, even though it's safer:
    • Its type requires you to check whether it's None before you can use it.

Lifetime

  • A lifetime is some stretch of your program for which a reference could be safe to use: a statement, an expression, the scope of some variable, or the like.
  • Lifetimes are entirely figmnets of Rust's compile-time imagination. At run-time,a reference is nothing but an addressl its lifetime is part of its type and has no run-time representation.

Static variables:

  • Every static must be initialized
  • Mutable statics are inherently not thread-safe (after all, any thread can access a static at any time), and even in single thread programs, they can fall pery to other sorts of reentry problems.
  • For these reason, you may access a mutable static only within an unsafe block.
#![allow(unused)]
fn main() {
static mut STASH: &i32 = &128;

fn f(p: &i32) {
    unsafe {
        STASH = p;
    }
}
}

The signature of f as written is actually standard for following:

#![allow(unused)]
fn main() {
fn f<'a>(p: &'a i32) {...}
}

You can read <'a> as "for any lifetime 'a" so when we write fn f<'a>(p: &'a i32), we're defining a function that takes a reference to an i32 with any given lifeetime 'a. Since STASH has a static lifetime, assigning a variable to it which is not static is gonna break Rust lifetime rules!

#![allow(unused)]
fn main() {
statis mut STASH: &i32 = & 10;

fn f(p: &'static i32) {
    unsafe {
        STASH = p;
    }
}
}

Question: What is the reentry we discussed above? Please refer to: here


Whenever a reference type appears inside another type's definition, you must write out its lifetime.