2

I'm trying to use a C library that requires me to give it strings (const char*) as function arguments, among other things.

I tried writing 2 functions for this (Rust -> C, C -> Rust), so I'm already aware CString and CStr exist, but couldn't get them working for the 1st conversion.

I tried using solutions from Stack Overflow and examples from the docs for both of them but the result always ends up garbage (mostly random characters and on one occasion the same result as in this post).

// My understanding is that C strings have a \0 terminator, which I need to append to input 
// without having the variable created deallocated/destroyed/you name it by Rust right after
// I don't have any other explanation for the data becoming garbage if I clone it.
// Also, this conversion works if i manually append \0 to the end of the string at the constructor
pub unsafe fn convert_str(input: &str) -> *const c_char {
    let c_str = input.as_ptr() as *const c_char;
    return c_str
}

// Works, at least for now
pub unsafe fn cstr_to_str(c_buf: *const i8) -> &'static str {
    let cstr = CStr::from_ptr(c_buf);
    return cstr.to_str().expect("success");
}

The resulting implementation acts like this:

    let pointer = convert_str("Hello");
    let result = cstr_to_str(pointer);
    println!("Hello becomes {}", result);
    // Output:
    // Hello becomes HelloHello becomescannot access a Thread Local Storage value during or after destruction/rustc/fe5b1...
    // LOOKsrc/main.rsHello this is windowFailed to create GLFW window.

How do I fix this? Is there a better way to do this I'm not seeing?

1 Answer 1

3
  • Rust strings don't have a \0 terminator, so to append one, convert_str must necessarily allocate memory (since it can't modify input - even in unsafe code, it can't know whether there's space for one more byte in the memory allocated for input)
  • If you're gonna wrangle C strings, you're gonna have to do C style manual management, i.e. together with returning a char*, convert_str also has to return the implicit obligation to free the string to the caller. Said differently, convert_str can't deallocate the buffer it must allocate. (If it did, using the pointer it returns would be a use after free, which indeed results in garbage.)

So your code might look like this:

  • Allocate a new CString in convert_str with CString::new(input).unwrap() and make sure its internal buffer doesn't get dropped at the end of the function with .into_raw()
  • Deallocate the return value of convert_str when you're done using it with drop(CString::from_raw(pointer)).

Playground

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.