liveBook · Manning

Reborrowing

As explained in Refactoring to Rust

Reborrowing is the conversion of a raw pointer into a Rust reference after the pointer's validity conditions have been established.

Reborrowing turns a raw pointer into a Rust reference. In an NGINX callback, the request arrives as a raw pointer, but the function that reads its body takes a reference. After checking that the pointer is not null, `let request = &*r;` dereferences it with `*` and takes a reference with `&`. section 4.4

The pointer and reference are the same at runtime; the difference is what Rust assumes about them. A reference must point to aligned, initialized data, must not be null, and has a lifetime during which it remains valid. A null check establishes only one of those conditions. Creating the reference from a raw pointer still requires care in unsafe code, even though ordinary code can then use the reference without dereferencing the raw pointer repeatedly. section 4.4

unsafe extern "C" fn read_body_handler(r: *mut ngx_http_request_t)
{
  if r.is_null() {
    eprintln!("got null request in body handler");
    return;
  }

  let request = &*r;

  let body = match request_body_as_str(request) {
    Ok(body) => body,
    Err(e) => {
      eprintln!("failed to parse body: {}", e);
      return;
    }
  };

  eprintln!("Read request body: {:?}", body);
}

See the callback’s null check, `&*r` conversion, and call to a body-reading function that accepts the resulting reference. section 4.4

When the code needs to modify a value, it can make a mutable reborrow instead. After checking that an NGINX-allocated buffer pointer is not null, `let buf = &mut (*buf_p);` gives the response-writing code a mutable reference for setting buffer fields. This avoids repeating raw-pointer dereferences. section 4.6

let buf_p =
    ngx_pcalloc(request.pool, std::mem::size_of::<
      ngx_buf_t>() as size_t)
      as *mut ngx_buf_t;
  if buf_p.is_null() {
    return Err("Failed to allocate buffer");
  }

  let buf = &mut (*buf_p);

  buf.set_last_buf(1);
  buf.set_last_in_chain(1);
  buf.set_memory(1);

See the allocation, null check, mutable reborrow, and subsequent buffer-setting calls together. section 4.6

How Refactoring to Rust covers it

How deep it goes
Explains the operators and reference validity assumptions through two conversions, but gives no general procedure for proving pointer validity.
Examples
Shows `&*r` in a request-body callback and `&mut (*buf_p)` after an NGINX buffer allocation.
What it assumes
Builds on the `&` reference operator and introduces `*` by comparison with dereferencing in C, C++, and Go.
What you can do after
Practice/perform reborrowing when passing checked FFI pointers to reference-taking Rust code or modifying an allocated buffer.
How it connects
Connects raw pointers, Rust references, lifetimes, unsafe code, and C foreign-function interfaces.

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.