liveBook · Manning

Reference

As explained in Refactoring to Rust

A pointer is a value representing a memory address that can be used to access data indirectly.

A reference lets Rust find and use a value without taking ownership of it. Borrowing creates a reference: `&value` borrows a value, and a parameter such as `art: &Artwork` accepts that borrow rather than moving the artwork into the function. section 2.3.1

Listing 2.9 A program admiring the same art twice
struct Artwork {
  name: String,
}

fn admire_art(art: &Artwork) {  #1
  println!("Wow, {} really makes you think.", art.name);
}

fn main() {
  let art1 = Artwork { name: "The Ordeal of Owain".to_string() };

  admire_art(&art1);  #2
  admire_art(&art1);
}

The same artwork is passed to `admire_art` twice because the function accepts a reference instead of taking ownership. section 2.3.1

Two rules keep borrowed access valid:

  • A value may have either one mutable reference or any number of immutable references at a time—not both. section 2.3.1
  • A reference must point to a valid value. Moving the value while a reference is still needed, or returning a reference to a local value that will be dropped, fails compilation. section 2.3.3

References are values with lifetimes of their own. When a function returns a reference borrowed from an input, the returned reference remains tied to that input. With one borrowed input, Rust can infer the relationship; with multiple borrowed inputs, a lifetime annotation can specify which input the result borrows from. For example, `fn get_value<'a>(numbers: &'a Vec<i32>, s: &str) -> &'a i32` ties the result to `numbers`, not `s`. It does not make the reference live longer than `numbers`. section 4.4.1

A raw pointer also holds a memory address, but unlike a Rust reference it carries no lifetime or ownership information the compiler can use to establish validity. Dereferencing one therefore requires `unsafe`, and the programmer must check that it points to valid memory. In C-facing code, a Rust function can receive `*const c_char` for input and `*mut c_int` for an output value; check untrusted pointers for null before dereferencing them. A null dereference is undefined behavior. section 3.1.1 section 3.2 section 3.2.2

#[no_mangle]
pub extern "C" fn solve(
    line: *const c_char, solution: *mut c_int) -> c_int {
  if solution.is_null() {  #1
    return 1;
  }

  unsafe {
    *solution = 1024;  #2
  }

  0
}

The C-compatible `solve` function checks its output pointer for null, then writes through it inside an `unsafe` block. section 3.2.2

How Refactoring to Rust covers it

How deep it goes
Explains borrowing rules, invalid lifetimes, raw-pointer dereferencing, and lifetime annotations, while leaving raw-pointer validity checks to the programmer.
Examples
Uses repeated artwork borrows, a returned vector-element reference, and a C calculator’s output pointer.
What it assumes
Introduces references through ownership and moves, then develops raw pointers through small Rust and C examples.
What you can do after
Apply it via Rust borrowing and C FFI to reuse values, identify dangling references, and check pointers before dereferencing.
How it connects
Connects references to ownership, borrowing, mutability, lifetimes, raw pointers, unsafe Rust, and C foreign function interfaces.

Also explained in

Compare how 17 books explain reference →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Refactoring to Rust. The section links point to the parts of the book it draws on.