liveBook · Manning

CStr

As explained in Refactoring to Rust

CStr is Rust's standard-library borrowed string type for representing nul-terminated C strings without taking ownership of their storage.

A C caller can pass a nul-terminated string to Rust without asking Rust to copy or own its storage. In a calculator, the input line lives in a C stack-allocated character array. Rust cannot take ownership of that array; it needs a read-only borrow that ends when the call returns. `CStr` provides that borrowed view. section 3.2.2

At the FFI boundary, check the input pointer for null, then use `unsafe { CStr::from_ptr(line) }` to construct the `CStr`. Call `to_str()` to obtain an `&str`. This step validates UTF-8: if the C input is not valid UTF-8, handle the error rather than treating it as a Rust string. The unsafe call leaves responsibility for memory safety with the developer. section 3.2.2 section 3.3

use libc::{c_char, c_int};
use std::ffi::CStr;

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

  let c_str = unsafe { CStr::from_ptr(line) };  #1
  let r_str = match c_str.to_str() {  #2
    Ok(s) => s,
    Err(e) => {
      eprintln!("UTF-8 Error: {}", e);
      return 1;
    },
  };

  println!("line: {}", r_str);

  unsafe {
    *solution = 1024;
  }

  0
}

The `solve` wrapper checks both pointers, borrows the input through `CStr`, and returns an error on invalid UTF-8. Its fixed solution value is only a placeholder for calculator logic. section 3.2.2

The resulting `&str` still points into the C buffer. Comparing its data pointer with the original C pointer confirms that the example does not reallocate the input string. Keep the borrowed view within the call rather than trying to make Rust own the caller’s stack memory. section 3.2.2

How Refactoring to Rust covers it

How deep it goes
Works through null checks, unsafe construction, UTF-8 error handling, and pointer comparison; leaves calculator logic as a placeholder.
Examples
Uses calculator input and matching C and Rust data pointers to demonstrate shared storage.
What it assumes
Starts with a C character array and introduces Rust borrowing, unsafe conversion, and UTF-8 checking at the FFI boundary.
What you can do after
Apply it via `std::ffi::CStr::from_ptr` and `to_str` to read borrowed C input as a validated Rust string.
How it connects
Connects C FFI, raw pointers, Rust borrowing, UTF-8 validation, and `&str`.

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.