liveBook · Manning

Unsafe Rust

As explained in Refactoring to Rust

Unsafe Rust is Rust code that permits operations the compiler cannot fully verify for memory safety and therefore places additional correctness responsibility on the programmer.

Unsafe Rust permits operations the compiler cannot fully verify for memory safety. It does not remove Rust’s safety rules; it makes the programmer responsible for upholding them where compiler checking stops. This is useful when integrating with dynamically allocated memory from C or other code outside Rust. section 3.0 section 3.1

Unsafe blocks and functions enable five categories of otherwise forbidden operation:

Reading a C-managed request body

An NGINX callback receives a raw request pointer across the C FFI boundary. The unsafe handler checks for a null pointer, converts it to a reference, validates request-body buffers, creates a byte slice with std::slice::from_raw_parts, and parses UTF-8. The surrounding code returns errors instead of assuming that foreign memory is initialized as expected. section 4.4

Listing 4.8 Request handler that can read off the request body
include!(concat!(env!("OUT_DIR"), "/nginx.rs"));

#[no_mangle]
pub unsafe extern "C" fn ngx_http_calculator_handler(  #1
  r: *mut ngx_http_request_t,
) -> ngx_int_t {
  let rc = ngx_http_read_client_request_body(
    r, Some(read_body_handler));  #2
  if rc != 0 {
    return rc;
  }

  0
}

unsafe extern "C" fn read_body_handler(
    r: *mut ngx_http_request_t) {  #3
  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); #4
}

unsafe fn request_body_as_str<'a>(  #5
  request: &'a ngx_http_request_t,
) -> Result<&'a str, &'static str> {
  if request.request_body.is_null()
    || (*request.request_body).bufs.is_null()
    || (*(*request.request_body).bufs).buf.is_null()
  {
    return Err("Request body buffers
      were not initialized as expected");
  }

  let buf = (*(*request.request_body).bufs).buf;

  let start = (*buf).pos;
  let len = (*buf).last.offset_from(start) as usize;

  let body_bytes = std::slice::from_raw_parts(start, len);

  let body_str = std::str::from_utf8(body_bytes)
    .map_err(|_| "Body contains invalid UTF-8")?;

  Ok(body_str)
}

Inspect the complete request-body handler, including C-compatible signatures, null checks, pointer dereferencing, buffer validation, and UTF-8 conversion. section 4.4

How Refactoring to Rust covers it

How deep it goes
Defines five permitted operations and works through raw-pointer handling, buffer validation, callbacks, and UTF-8 conversion.
Examples
Uses an NGINX request callback that checks pointers, reads foreign buffers, constructs a byte slice, and validates UTF-8.
What it assumes
Assumes basic Rust programs and ownership and lifetime concepts while introducing foreign-memory handling through concrete integration tasks.
What you can do after
Apply it via C FFI by writing unsafe callbacks that handle foreign pointers and validate externally managed memory.
How it connects
Connects unsafe Rust with C FFI, raw pointers, ownership, lifetimes, callbacks, and memory safety.

Also explained in

Compare how 4 books explain unsafe Rust →

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.