Lifetime of Slice Constructed from Pointer

I am calling Rust code from C code. In the Rust code a &str is constructed from a pointer and a length. This is what I have now:

fn ptr_to_str(ptr: *const u8, len: usize) -> Option<&'static str> {
    if ptr.is_null() {
        return None;
    }

    let slice = unsafe { slice::from_raw_parts(ptr, len) };
    str::from_utf8(slice).ok()
}

In this case it does not really matter that much, but I don't really like using the static lifetime. Is there a better alternative?

You’d need to get the lifetime from somewhere else, for example:

unsafe fn ptr_to_str<'a, T:?Sized>(_virtual_owner: &'a T, ptr: *const u8, len: usize) -> Option<&'a str> {
    if ptr.is_null() {
        return None;
    }

    let slice = slice::from_raw_parts(ptr, len);
    str::from_utf8(slice).ok()
}

Here, the compiler will prevent the returned &str from being used outside the region where _virtual_owner is a valid reference.

3 Likes

You can parameterize the function on the lifetime (fn ptr_to_str<'a>(…) -> &'a str). However, this does not change the fact that this is fundamentally unsafe. You should mark this function as unsafe.

2 Likes

A practical way to do this is to use the pointer itself as the owner, thus borrowing it rather than copying it:

unsafe
fn ptr_to_str<'ptr> (&ptr: &'ptr *const u8, len: usize)
  -> Option<&'ptr str>
{
    if ptr.is_null() {
        return None;
    }

    let slice = slice::from_raw_parts(ptr, len);
    str::from_utf8(slice).ok()
}

This will "ensure" your obtained &str can only be used within the body of the ffi-exported function, and no more (e.g., because the FFI may immediately free the string when the function returns).

5 Likes