Course contentsShow
← Rust
Lesson 1466 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Handling C Strings in Rust

You will safely convert C-style null-terminated strings into Rust-native string slices.

In this lesson, you will learn how to safely bridge the gap between null-terminated C strings and Rust’s memory-safe string slices without violating ownership rules.

The Problem: Null-Termination vs. Fat Pointers

In C, a string is a pointer to a sequence of bytes ending with a 0x00 (null) byte. Rust strings, conversely, are "fat pointers"—a struct containing a pointer to the data and a length. When interacting with C libraries via FFI, you often receive a *const c_char. If you simply wrap this in a slice, Rust has no way of knowing where the data ends, leading to potential buffer overreads.

Using CStr to Bridge the Gap

To safely convert a raw C pointer to a Rust-native reference, we use the std::ffi::CStr type. It acts as a wrapper that performs the necessary validation.

use std::ffi::CStr;
use std::os::raw::c_char;

unsafe fn process_c_string(ptr: *const c_char) {
    // 1. Create a CStr wrapper (checks for null and finds the terminator)
    let c_str = CStr::from_ptr(ptr);
    
    // 2. Convert to a Rust string slice (&str)
    // to_str() returns a Result to ensure the data is valid UTF-8
    if let Ok(rust_str) = c_str.to_str() {
        println!("Received: {}", rust_str);
    }
}

Why this is safe

The CStr::from_ptr function performs a scan for the null terminator. Because this operation is unsafe (the compiler cannot prove the pointer points to valid memory or is correctly terminated), the onus is on you to ensure the pointer is valid for the duration of the CStr reference. Once wrapped, to_str() handles the final transformation, enforcing UTF-8 validity.

If your C string might contain invalid UTF-8 (common in C legacy code), you can use to_string_lossy() instead, which replaces invalid sequences with the replacement character ``, ensuring your Rust program never crashes due to decoding errors.

Key Takeaway: Use CStr::from_ptr to safely wrap null-terminated buffers, and always validate the result as a UTF-8 slice via to_str() to bridge the gap between raw pointer logic and Rust’s memory-safe string abstractions.