Is this technically sound?

We wanna have this code:

unsafe fn foo<'a>(ptr: *const c_char, bogus: PhantomData<&'a ()>) {
  let first = unsafe { CStr::from_ptr::<'a>(ptr) }.to_str().map(|x| x.to_owned());
  unsafe { mutate_ffi_string(); }
  let second = unsafe { CStr::from_ptr::<'a>(ptr) }.to_str().map(|x| x.to_owned());
}

Is this okay?

The function should be sound, assuming that ptr is valid for reads up to the first null byte, and that neither the OS nor any other thread modifies it between the CStr::from_ptr() and str::to_owned() calls. If you're referring to CStr::from_ptr()'s safety comment,

  • The memory referenced by the returned CStr must not be mutated for the duration of lifetime 'a.

then I think it's just inaccurate: the &CStr simply must not be used after the underlying memory is mutated. Types can't specialize on lifetimes, so unsafe code can manipulate them arbitrarily.

Yeah we do think the safety comment should be about the returned reference rather than the lifetime. (e.g. "for the duration of the reference" or something.)

We got weird looks elsewhere when we brought it up tho.

Is there a reason to specify the lifetime there since you're immediately discarding the CStr?

CStr::from_ptr()'s safety comment.

I am by no means an unsafe expert but I'm pretty sure that only applies if you're keeping the CStr around.

You're not even binding the CStr so I think you should be fine without attaching a lifetime.

If you're doing complicated things with it you could end up accidentally using it after the original pointer gets invalidated since it infers an arbitrary lifetime. But you're immediately copying from it and dropping it so that isn't a concern for you.

The safety comment is stricter than what the method actually requires.

2 Likes