Sorry for the late reply, I didn't see this thread at the time (btw, anybody is welcome to directly ping me in this forum when talking about safer-ffi
); and while I had afterwards noticed the GH issue, I have been quite busy to properly tackle it (I suspect I may have to add extra raw APIs to attend to your current need).
Context
A few remarks to set some context:
-
the c_char
of the stdlib/libc
, much like c_int
, and much unlike usize
, is a type
alias for another primitive type depending on the platform. I dislike such a design quite a bit, to be honest: imagine if we had had type usize = u32/u64;
depending on the platform!
-
safer-ffi
thus defines an internal c_char
new type
(new struct), which is a transparent wrapper around an 8-bit integer, but which, when translated to C headers, yields char
rather than int8_t
or uint8_t
(but byte
for C#).
-
safer-ffi
emits C-compatible headers of otherwise "Rust idioms". That is, for instance, it considers that "stuff" is either:
- owned (e.g., heap-allocated),
- borrowed exclusively-and-thus-mutably,
- or borrowed in a shared-fashion and "thus" immutably.
Hence, for instance, the existence of c_slice::{Ref,Mut,Box}<u8>
to represent, respectively, &[u8]
, &mut [u8]
, and Box<[u8]>
.
However, in the case of str
, both in the Rusty wide pointer case (str::Ref
and str::Box
respectively), and in the C thin pointer case (char_p::Ref
and char_p::Box
respectively), I have semi-deliberately omitted the &mut
case.
-
The reason for this is that these types represent UTF-8 encoded strings (with the extra constraint of the NULL terminator in the char_p
cases), and mutating these is footgun-prone, given how (Rust) char
s / Unicode code points have varying-width UTF-8 encodings.
- For instance, in Rust, seeing a
&mut str
is extremely rare.
But I could get by the idea of the C user only wanting to deal with ASCII strings, which a fortiori, are UTF-8 strings, and mutable access to ASCII strings is less error-prone (e.g., a &mut [AsciiByte]
kind of API).
Or, unsafe
ly, exposing a &mut str
much like Rust's stdlib does.
To answer the OP
Be it as it may, my answer to the OP would thus be:
-
usually, *mut c_char
is used to represent an owned C string, and in safer-ffi
, this would be a char_p::Box
;
-
I could also get by the idea of defining a char_p::Malloc
type, so that release of the pointer is done with free
rather than with GlobalAlloc::dealloc(ptr, strlen(p))
(which is what char_p::Box
does, and which may not match free
unless the GlobalAlloc
is registered as malloc/free
).
#[derive_ReprC]
#[repr(transparent)]
pub struct char_p_malloc(
ptr::NonNullOwned<::safer_ffi::c_char>,
);
impl char_p_malloc {
pub fn new(s: &str) -> Result<Self, InvalidNulTerminator> {
if … // check against truncation
let ptr =
ptr::NonNull::new(unsafe { ::libc::strndup(s.as_ptr(), s.len()) }))
.expect("`strndup` returned NULL")
;
Self(ptr::NonNullOwned(ptr, PhantomData))
}
}
// and/or `Deref`.
impl char_p_malloc {
pub fn as_ref(&self) -> char_p::Ref<'_> {
char_p::Ref((*self.0).into())
}
}
impl Drop for char_p_malloc {
fn drop(&mut self) {
unsafe { ::libc::free(self.0.as_mut_ptr().cast()) }
}
}
-
but if you do need a C equivalent of *mut c_char
(which correctly uses char
in the C headers), but which does not represent ownership over the given C string, then I could expose a low-level/unsafe
type to do so;
- (or this
AsciiByte
abstraction idea)
For instance, I should very much expose the aforementioned c_char
type that safer-ffi
internally uses (so that, worst case, with some unsafe
, you can roll your own *mut safer_ffi::c_char
yourself).
Aside about char_p::Raw
-
By the way, char_p::Raw
is not intended to be used as a *mut c_char
, but rather, as a char_p::Ref<'erased>
, since for technical reasons, extern "C" fn (char_p::Ref<'_>)
does not implement ReprC
despite the impl<A: …> ReprC for extern "C" fn(A)
.
Thence the need to internally use unsafe extern "C" fn(char_p::Raw)
, which could soundly be called with a s: char_p::Ref<'_>
by doing s.into()
.
Nowadays, though, the expected way to handle this limitation is to write:
#[derive_ReprC]
#[repr(transparent)]
pub struct MyHigherOrderCb /* = */ {
c_fn: pub extern "C" fn(char_p::Ref<'_>),
}
to let the derive manually implement ReprC
for this newtype wrapper type (thereby removing the need for unsafe
).