Why the value from CString::into_raw() can't be freed?

Hi, I am doing some FFI work and I have to deal with strings. So I came across of CString's intro_raw() function and its documentation states that:

The pointer which this function returns must be returned to Rust and reconstituted using
from_raw to be properly deallocated. Specifically, one should not use the standard C free() 
function to deallocate this string.

I know that Rust now uses system’s allocator, but why that value can’t be freed from C using free() function?
I am sure that I am missing something, thanks for the answers!

Imagine that String::new worked like this (simplified / sketchy example):

struct Box<str> {
    ptr: *mut u8,
    len: usize,
}

impl Box<str> {
    fn new (s: &str) -> Self {
        let len = s.len();
        let ptr = malloc(len + 32).add(32);
        ptr::copy(ptr, s.as_ptr(), len);
        Self { ptr, len }
     }

     fn into_raw (self) -> *mut u8
     {
          let ptr = self.ptr;
          mem::forget(self);
          ptr
     }
}

impl Drop for Box<str> {
    fn drop (&mut self)
    {
        free(self.ptr.sub(32));
    }
}

The point is, that even when Rust uses malloc and free, we don’t know how it may use it (not part of the api), so we cannot go and free an opaque “handle” pointer the api gives us.

1 Like

There’s a much simpler example why you can’t do this.

fn main() {
    let p = Box::new(vec![1, 2, 3]).into_raw();

    // even if this did successfully deallocate the Box,
    // the Vec will be leaked!
    unsafe { libc::free(p); }
}
3 Likes

Another more subtle reason is that you cannot generally guarantee that a foreign library is using the same allocator as the Rust code that allocates a CString (or any other kind of allocation for that matter). Even if both libraries are using the system allocator, dynamic linking and platform weirdness means that they might be using different instances of the allocator.

5 Likes