use std::ffi::{CStr, CString}; use std::os::raw::c_char; // --- C functions we pretend to link against --- // In a real scenario, these would be in a C library and linked using `build.rs` // For this snippet, we define them directly for demonstration. extern "C" { // C function to calculate string length (like strlen) fn my_c_strlen(s: *const c_char) -> usize; // C function to print a string fn my_c_puts(s: *const c_char) -> i32; } // --- Our safe Rust wrapper functions --- /// Safely calculates the length of a C-style string. /// # Safety /// The `ptr` must be a valid, null-terminated C string. #[no_mangle] pub extern "C" fn my_c_strlen(s: *const c_char) -> usize { // In a real C implementation, this would iterate until null terminator. // For demo, we just return a fixed length if valid. if s.is_null() { return 0; } unsafe { CStr::from_ptr(s).to_bytes().len() } } /// Safely prints a Rust string using a C `puts` equivalent. /// # Safety /// The `s` string must not contain interior null bytes if passed to a C function /// that expects a null-terminated string without embedded nulls. /// The C function `my_c_puts` must correctly handle the `*const c_char`. #[no_mangle] pub extern "C" fn my_c_puts(s: *const c_char) -> i32 { // In a real C implementation, this would print to stdout. // For demo, we print via Rust. if s.is_null() { eprintln!("Attempted to print null C string!"); return -1; } let c_str = unsafe { CStr::from_ptr(s) }; match c_str.to_str() { Ok(rust_str) => { println!("C Puts: {}", rust_str); 0 } Err(_) => { eprintln!("C Puts: Invalid UTF-8 sequence in C string!"); -1 } } } pub fn safe_strlen(s: &str) -> usize { let c_str = CString::new(s).expect("String should not contain interior nulls"); // This `unsafe` block calls the `extern "C"` function. // It's considered safe because we've ensured `c_str` is valid // and null-terminated for the C function. unsafe { my_c_strlen(c_str.as_ptr()) } } pub fn safe_puts(s: &str) { let c_str = CString::new(s).expect("String should not contain interior nulls"); unsafe { my_c_puts(c_str.as_ptr()); } } fn main() { let rust_string = "Hello from Rust via C!"; let len = safe_strlen(rust_string); println!("Length of '{}' (via C strlen): {}", rust_string, len); safe_puts(rust_string); let another_string = "Another message"; safe_puts(another_string); // Example of a string with an interior null byte (would panic on CString::new) // let bad_string = "Hello\0World"; // let _ = CString::new(bad_string); // This line would panic }