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?
}
std::ffi
module, namely CStr
and CString
.
CStr is a dynamically sized type, so it can only be used by pointers. This makes it very similar to the regular str type. You can use the unsafe CStr::from_ptr code>The static method is constructed from *const c_char & CStr. This method is not safe because the original pointer passed to it is not guaranteed to be valid, it does point to a valid C string and the lifetime of the string is correct.
You can use its to_str()
method to obtain & str.
This is an example:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;
use std::str;
extern {
fn hello() -> *const c_char;
}
fn main() {
let c_buf: *const c_char = unsafe {hello()} ;
let c_str: &CStr = unsafe {CStr::from_ptr(c_buf) };
let str_slice: &str = c_str.to_str().unwrap();
let str_buf: String = str_slice .to_owned(); // if necessary
)
You need to consider the lifetime of *const c_char pointers and who owns them. According to the C API, you may need to call special on the string Release the function. You need to carefully arrange the conversion so that the slice does not exceed the pointer. The fact that CStr::from_ptr returns &CStr with an arbitrary lifetime is helpful here (although it is inherently dangerous); for example, you can replace the C character The string is encapsulated into a structure and Deref conversion is provided, so that you can use the structure like a string slice:
extern crate libc;
use libc ::c_char;
use std::ops::Deref;
use std::ffi::CStr;
extern "C" {
fn hello( ) -> *const c_char;
fn goodbye(s: *const c_char);
}
struct Greeting {
message: *const c_char,
}
impl Drop for Greeting {
fn drop(&mut self) {
unsafe {
goodbye(self.message);
}
}
}
impl Greeting {< br /> fn new() -> Greeting {
Greeting {message: unsafe {hello()} }
}
}
impl Deref for Greeting {< br /> type Target = str;
fn deref<'a>(&'a self) -> &'a str {
let c_str = unsafe {CStr::from_ptr(self .message) };
c_str.to_str().unwrap()
}
}
There is another type in this module, called CString
. It has the same relationship with CStr, string and str-CString is the owned version of CStr. This means that it "holds" the handle allocated by byte data, and discarding CString will release the memory it provides ( Basically, CString wraps Vec
Unfortunately, C strings always end with zero bytes and cannot contain a string in them, while Rust&[u8]/Vec
Similar to String that implements Derefas_ptr()
method that returns *const c_char required for C interoperability is defined on CStr. You can call this method directly on the CString value, which is very convenient.
CString can be created from everything that can be converted to Vec
extern crate libc;
use libc::c_char;
use std::ffi::CString;
fn main() {
let c_str_1 = CString::new("hello").unwrap(); // from a &str, creates a new allocation
let c_str_2 = CString::new(b"world "as &[u8]).unwrap(); // from a &[u8], creates a new allocation
let data: Vec= b"12345678".to_vec(); // from a Vec , consumes it
let c_str_3 = CString::new(data).unwrap();
// and now you can obtain a pointer to a valid zero-terminated string
// make sure you don't use it after c_str_2 is dropped
let c_ptr: *const c_char = c_str_2.as_ptr();
// the following will print an error message because the source data
// contains zero bytes
let data: Vec= vec![1, 2, 3, 0, 4, 5, 0, 6];
match CString::new(data) {
Ok(c_str_4) => println!("Got a C string: {: p}", c_str_4.as_ptr()),
Err(e) => println!("Error getting a C string: {}", e),
}
} pre>If you need to transfer the ownership of CString to C code, you can call
CString::into_raw
. Then you need to return the pointer and release it in Rust; the Rust allocator is not too It may be the same allocator used by malloc and free. All you need to do is callCString::from_raw
, and then allow the string to be deleted normally.
I am trying to get the C string returned by the 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 use C strings in Rust is to use std::ffi
The structure in the module, namely CStr
and CString
.
CStr is a dynamic size type, so it can only pass Pointer usage. This makes it very similar to the regular str type. You can use the unsafe CStr::from_ptr
static method to construct from *const c_char & CStr. This method is not safe because it is not guaranteed to be passed to Its original pointer is valid, it does point to a valid C string and the lifetime of the string is correct.
You can use its to_str()
method to obtain & str.
This Is an example:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;< br />use std::str;
extern {
fn hello() -> *const c_char;
}
fn main() {
let c_buf: *const c_char = unsafe {hello() };
let c_str: &CStr = unsafe {CStr::from_ptr(c_buf) };
let str_slice: &str = c_str. to_str().unwrap();
let str_buf: String = str_slice.to_owned(); // if necessary
}
You need to consider the lifetime of *const c_char pointer and Who owns them. According to the C API, you may need to call a special release function on the string. You need to carefully arrange the conversion so that the slice does not exceed the pointer. The fact that CStr::from_ptr returns & CStr with an arbitrary lifetime is in This is helpful (although it is inherently dangerous); for example, you can encapsulate the C string into a structure and provide Deref conversion so that you can use the structure as you would use string slices:
extern crate libc;
use libc::c_char;
use std::ops::Deref;
use std::ffi::CStr;
extern "C" {
fn hello() -> *const c_char;
fn goodbye(s: *const c_char);
}
struct Greeting {
message: *const c_char,
}
impl Drop for Greeting {
fn drop(&mut self) {
unsafe {
goodbye(self.message);
}
}
}
impl Greeting {
fn new() -> Greeting {
Greeting {message: unsafe {hello( )} }
}
}
impl Deref for Greeting {
type Target = str;
fn deref<'a>( &'a self) -> &'a str {
let c_str = unsafe {CStr::from_ptr(self.message) };
c_str.to_str().unwrap()
}
}
There is another type in this module, called CString
. It has the same relationship with CStr, string and str-CString is CStr Owning the version. This means that it "holds" the handle allocated by the byte data, and discarding the CString will release the memory it provides (basically, CString wraps Vec
Unfortunately, C strings always end with zero bytes and cannot contain a string in it, and Rust& ( u8]/Vec
With the implementation of Deref< Target = The String of str> is similar, CString implements Deref< Targe t = CStr>, so you can directly call the method defined on CStr on CString. This is important because the as_ptr()
that returns *const c_char required for C interoperability is defined on CStr Method. You can call this method directly on the CString value, which is very convenient.
CString can be created from everything that can be converted to VecCString::new()
. Of course, if you pass a byte slice or string slice, a new allocation will be created, and Vec
extern crate libc;
use libc::c_char;
use std::ffi::CString;
fn main() {
let c_str_1 = CString::new("hello").unwrap(); // from a &str, creates a new allocation
let c_str_2 = CString ::new(b"world" as &[u8]).unwrap(); // from a &[u8], creates a new allocation
let data: Vec= b"12345678".to_vec (); // from a Vec , consumes it
let c_str_3 = CString::new(data).unwrap();
// and now you can obtain a pointer to a valid zero-terminated string
// make sure you don't use it after c_str_2 is dropped
let c_ptr: *const c_char = c_str_2.as_ptr();
// the following will print an error message bec ause the source data
// contains zero bytes
let data: Vec= vec![1, 2, 3, 0, 4, 5, 0, 6];
match CString::new(data) {
Ok(c_str_4) => println!("Got a C string: {:p}", c_str_4.as_ptr()),
Err(e) => println!("Error getting a C string: {}", e),
}
}
If you need to transfer the ownership of CString to C code, you can call< code>CString::into_raw. Then you need to return the pointer and release it in Rust; the Rust allocator is unlikely to be the same as the allocator used by malloc and free. All you need to do is call CString ::from_raw
, and then allow the normal deletion of the string.