Taking Ownership from a C String Retrieved from FFI

I have a library that exposes its functionality over an FFI. In the library I have a custom struct

pub struct Item {
    title: String
}

that is created by an FFI exposed function

#[no_mangle]
pub extern "C" fn new_item(title: *const c_char) -> *const Item;

While the item is returned to the caller, they can't really do anything with it, except hold onto it and then pass it on to another FFI function do_stuff_on_the_internet, which uses the item in question and then frees the memory at the end.

Now here's my question: I want to take ownership of the passed string, preferably without copying it. I already read "How do you correctly create an owned String from *const c_char?," but there the string is cloned into an owned string.
Is this even possible, or should I just clone the string and leave the original to the caller to to whatever?

You can't soundly free something allocated[1] by the foreign language in Rust, if that's the question. They could have different allocators/deallocators.


  1. or not allocated! ↩︎

So, the best approach would be to take the string, and either convert it via &CStr into a &str,i.e.

pub struct Item<'a> {
    title: &'a str
}

or clone it into a String?

Roughly, that's the idea.

But you probably don't want the lifetime; *const str may be an option. Box<str> is probably also a better choice than String for the owned case.

And you haven't shown your logic around creating the *const Item (maybe Box::into_raw?). If you need to deallocate the allocated-in-Rust Item as well, the caller will have to return the value in any case (or leak memory). And they won't be able to deallocate the *const c_char until after they return the value if you don't clone the data. I.e.

  • If you don't want to clone the data

    • Caller can't[1] modify the data until returning the value[2]
    • Caller must return the value somehow or will leak the Item
    • Caller can't use the returned value after they return it or after they deallocate the data
    • (If they don't want to leak memory, they must return the value and then deallocate the data)
  • If you do want to clone the data

    • Caller can modify or deallocate the original data immediately
    • Caller must return the value somehow or will leak the Item and the cloned data

Alternatively you can make Item FFI-safe and return an owned Item. A *const u8 and usize would cover both the *const str and Box<str> cases. (With String you need a second usize for capacity.) Then,[3]

  • If you don't want to clone the data

    • Caller can use the returned value (e.g. multiple calls) until they deallocate or modify the data
    • Caller can't use the returned value after they deallocate or modify the data
    • If they don't want to leak memory, they must deallocate the data
      • But don't have to return the value
  • If you do want to clone the data

    • Caller can modify or deallocate the original data immediately
    • Caller must return the value somehow or will leak the cloned data
      • (no multiple calls allowed)

  1. or is very constrained anyway ↩︎

  2. e.g. if they make it non-UTF8 you may end up doing something UB ↩︎

  3. assuming no other reasons beyond ownership make multiple do_stuff_on_the_internet calls with the same value unsound ↩︎

(Assuming the C string is actually guaranteed to be UTF-8 encoded, which you can't take for granted.)

I made a note that the string has to be valid UTF-8 and check inside and then go into an error handling routine taken from Using Unsafe for Fun and Profit.

Yes, pretty much, I first create the object in Rust, then box it and call Box::into_raw afterwards.Here's a pseudo code control flow. Hope that helps:

// Caller
let item = new_item(1234321); // Creation function takes an id

// I use setters to safely set the properties, because we don't want a construction method
// that takes 6+ parameters. This is more readable.
set_title(item, "Foobar");

// Fill in other fields

let manager = new_metadata_manager();

// Pass the item to the manger that queries stuff from the internet and stores for future use.
// After this method has completed, the item should be freed by the manager
do_stuff_on_the_internet(manager, item);


// Callee/Rust Code
#[no_mangle]
pub extern "C" fn do_stuff_on_the_internet(item: *const Item) {
    // Do all the internet related query stuff. This is actually done in another module, but for 
    // brevity's sake, assume it's all done here.

    unsafe {
        // Null check was done elsewhere
        Box::from_raw(item);
    }
}

If the do_stuff_on_the_internet function were written in Rust and never exposed to the FFI, it would have

pub fn do_stuff_on_the_internet(item: Item);

as signature.

So basically my idea is that the user creates an Item and fills in the information they think is required. After that the item is passed to the do_stuff_on_the_internet method where the actual work is done.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.