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
