liveBook · Manning

Dereferencing

As explained in Refactoring to Rust

A pointer dereference is an operation that accesses the value stored at the memory address represented by a pointer.

A pointer holds a memory address. Dereferencing accesses the value at that address. In Rust, `*pointer` performs the operation: it can read a value, or write one when the pointer permits it. section 3.1.1 section 3.2.2

A raw pointer and a Rust reference both hold an address, but the compiler treats them differently. References carry information, including lifetimes, that lets the compiler enforce their validity. Raw pointers lack those guarantees, so dereferencing one requires an `unsafe` block. That block does not validate the address; the programmer must do so. section 3.1.1

Read a buffer through a raw pointer

Given `data: Vec<u8>` containing `5, 10, 15, 20`, pass `data.as_ptr()` and `data.len()` to a reader. For each index, `*slice_p.offset(index as isize)` reads the corresponding byte inside an `unsafe` block. The pointer identifies where the buffer starts; the length controls how many elements the loop attempts to read. section 3.1.1

Listing 3.1 Reading the elements of a vector using pointer arithmetic
fn main() {
  let data: Vec<u8> = vec![5, 10, 15, 20];  #1

  read_u8_slice(data.as_ptr(), data.len()); #2
}

fn read_u8_slice(slice_p: *const u8, length: usize) {  #3
  for index in 0..length {
    unsafe {   #4
      println!("slice[{}] = {}", index,
        *slice_p.offset(index as isize));  #5
    }
  }
}

Shows the complete vector-reading loop, including the pointer, length, offset, and `unsafe` dereference. section 3.1.1

Write a result back to C

A C caller passes `&solution` to a Rust `solve` function as a `*mut c_int` out parameter. A no-op `solve` leaves the displayed result unpredictable. First check `solution.is_null()` and return an error if it is null; then `unsafe { *solution = 1024; }` writes a test value for the caller to print. This verifies the write across the language boundary before implementing the calculation. A null check matters because dereferencing null is undefined behavior; it is not, by itself, a guarantee that every other pointer is valid. section 3.2.2 section 3.1.1

#[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
}

Shows the C-compatible `solve` function with its null check and isolated pointer write. section 3.2.2

Input pointers need care too. Before reading a C string passed as `*const c_char`, check for null, use `CStr::from_ptr` in an `unsafe` block, and handle conversion to a Rust string. That lets Rust read the C-owned string without allocating a second copy. section 3.2.2

How Refactoring to Rust covers it

How deep it goes
Defines the operation and works through pointer arithmetic, unsafe blocks, and null checks; it does not establish that a null check proves validity.
Examples
Uses a byte vector read by offset and a C calculator whose Rust function writes through a solution out parameter.
What it assumes
Builds on pointers and Rust references while explaining dereferencing and raw-pointer validity.
What you can do after
Apply it via Rust raw pointers to read a bounded buffer and write a C out parameter while checking for null.
How it connects
Connects raw pointers to Rust references, lifetimes, unsafe blocks, pointer arithmetic, C FFI, and null pointers.

Also explained in

Compare how 3 books explain dereferencing →

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.