tezvyn:

Rust: Bridging C Strings with CStr and CString

AI-drafted, machine-checkedSource: doc.rust-lang.orgintermediate

CString and CStr are Rust's safe wrappers for C's nul-terminated strings. CString builds a C-compatible string to pass *out* of Rust; CStr interprets one coming *in*. Use them for any FFI calls.

WHY IT EXISTS Rust strings (String, &str) and C strings are fundamentally incompatible. Rust strings are length-prefixed, always UTF-8, and can contain null bytes (\0). C strings are nul-terminated, have no defined encoding, and their length must be calculated by scanning. This mismatch requires a safe bridge for Foreign Function Interface (FFI) code to prevent memory errors and data corruption.

THE MENTAL MODEL Think of CString and CStr as diplomatic translators between Rust and C. CString takes a Rust string, validates it for C compatibility (no internal nulls), and packages it for C by adding a final null terminator. CStr takes a raw pointer from C, wraps it in a safe Rust slice, and provides tools to inspect it and convert it back to a Rust string, handling potential encoding errors. CString is for exporting strings to C; CStr is for importing strings from C.

HOW IT WORKS To pass a string from Rust to C, you create an owned CString from a Rust &str using CString::new(). This returns a Result, which will be an Err if the input contains \0 bytes. The CString manages a buffer that is guaranteed to be nul-terminated. You can then get a raw pointer from it using .as_ptr() to pass to the C function.

To receive a string from C, a C function gives you a *const c_char pointer. You wrap it in a borrowed CStr using the unsafe block CStr::from_ptr(). It's unsafe because Rust cannot guarantee the pointer is valid or properly nul-terminated. Once wrapped, CStr provides safe methods like .to_str(), which attempts to convert the byte slice to a Rust &str and returns a Result because the bytes may not be valid UTF-8.

WHEN TO USE IT Use these types whenever you pass strings across a C ABI boundary. This is critical when calling C libraries (like OpenSSL or SQLite) from Rust, or when exporting Rust functions to be called by C, Python, Ruby, or other languages that use C bindings.

WHEN NOT TO USE IT For purely Rust-to-Rust code, String and &str are always superior. For interacting with the operating system (e.g., file paths, environment variables), prefer OsString and OsStr. They correctly handle platform-specific string encodings which may be neither UTF-8 nor C-style nul-terminated strings.

ONE CANONICAL EXAMPLE To call a C function void print_msg(const char* msg);, you prepare the string in Rust: let rust_string = "Hello from Rust!"; let c_string = CString::new(rust_string).unwrap(); unsafe { print_msg(c_string.as_ptr()); }. To read a string from a C function const char* get_msg();, you would do: let raw_ptr = unsafe { get_msg() }; let c_str = unsafe { CStr::from_ptr(raw_ptr) }; let rust_str = c_str.to_str().expect("C string is not valid UTF-8");.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.