I'm trying to get a C string returned by a C library and convert it to a Rust string via FFI.
mylib.c
const char* hello(){
return "Hello World!";
}
main.rs
#![feature(link_args)]
extern crate libc;
use libc::c_char;
#[link_args = "-L . -I . -lmylib"]
extern {
fn hello() -> *c_char;
}
fn main() {
//how do I get a str representation of hello() here?
}
The best way to work with C strings in Rust is to use structures from the
std::ffi
module, namelyCStr
andCString
.CStr
is a dynamically sized type and so it can only be used through a pointer. This makes it very similar to the regularstr
type. You can construct a&CStr
from*const c_char
using an unsafeCStr::from_ptr
static method. This method is unsafe because there is no guarantee that the raw pointer you pass to it is valid, that it really does point to a valid C string and that the string's lifetime is correct.You can get a
&str
from a&CStr
using itsto_str()
method.Here is an example:
You need to take into account the lifetime of your
*const c_char
pointers and who owns them. Depending on the C API, you may need to call a special deallocation function on the string. You need to carefully arrange conversions so the slices won't outlive the pointer. The fact thatCStr::from_ptr
returns a&CStr
with arbitrary lifetime helps here (though it is dangerous by itself); for example, you can encapsulate your C string into a structure and provide aDeref
conversion so you can use your struct as if it was a string slice:There is also another type in this module called
CString
. It has the same relationship withCStr
asString
withstr
-CString
is an owned version ofCStr
. This means that it "holds" the handle to the allocation of the byte data, and droppingCString
would free the memory it provides (essentially,CString
wrapsVec<u8>
, and it's the latter that will be dropped). Consequently, it is useful when you want to expose the data allocated in Rust as a C string.Unfortunately, C strings always end with the zero byte and can't contain one inside them, while Rust
&[u8]
/Vec<u8>
are exactly the opposite thing - they do not end with zero byte and can contain arbitrary numbers of them inside. This means that going fromVec<u8>
toCString
is neither error-free nor allocation-free - theCString
constructor both checks for zeros inside the data you provide, returning an error if it finds some, and appends a zero byte to the end of the byte vector which may require its reallocation.Like
String
, which implementsDeref<Target = str>
,CString
implementsDeref<Target = CStr>
, so you can call methods defined onCStr
directly onCString
. This is important because theas_ptr()
method that returns the*const c_char
necessary for C interoperation is defined onCStr
. You can call this method directly onCString
values, which is convenient.CString
can be created from everything which can be converted toVec<u8>
.String
,&str
,Vec<u8>
and&[u8]
are valid arguments for the constructor function,CString::new()
. Naturally, if you pass a byte slice or a string slice, a new allocation will be created, whileVec<u8>
orString
will be consumed.If you need to transfer ownership of the
CString
to C code, you can callCString::into_raw
. You are then required to get the pointer back and free it in Rust; the Rust allocator is unlikely to be the same as the allocator used bymalloc
andfree
. All you need to do is callCString::from_raw
and then allow the string to be dropped normally.In addition to what @vladimir-matveev has said, you can also convert between them without the aid of
CStr
orCString
:Just make sure that when converting from a &str to a C string, your &str ends with
'\0'
. Notice that in the code above I usestrlen(c_s)+1
instead ofstrlen(c_s)
, sos
is"Hello World!\0"
, not just"Hello World!"
.(Of course in this particular case it works even with just
strlen(c_s)
. But with a fresh &str you couldn't guarantee that the resulting C string would terminate where expected.)Here's the result of running the code: