#![allow(non_camel_case_types)]
use libc::{c_uchar, size_t};
use std::str::FromStr;
use std::ffi::{CString, NulError};
use std::slice;
#[repr(C)]
pub struct c_str_
You've missed one crucial step.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.as_ptr() as *const u8, len: self.len() })
}
allocates a new CString
structure, and takes a pointer to its data, but that data will still be freed once the to_c_str
function runs to completion. This means that later code can overwrite the string contents in-memory. In your example case, it just happens to be that only the first character is overwritten.
I'd recommend reading over the documentation of .as_ptr() as it tries to cover some of this.
You could manually std::mem::forget
, e.g.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
let s = c_str_t { data: result.as_ptr() as *const u8, len: self.len() };
std::mem::forget(result);
Ok(s)
}
but the best approach would be to use .into_raw() to take ownership and return the pointer on its own.
fn to_c_str(&self) -> Result<c_str_t, NulError> {
let result = match CString::new(&self[..]) {
Ok(result) => result,
Err(e) => {
return Err(e);
}
};
Ok(c_str_t { data: result.into_raw() as *const u8, len: self.len() })
}