How to convert a C string into a RUST string and return via FFI?

I am trying to get the C string returned by the C library and convert it to a Rust string through 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 the structure in the 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_ptrThe 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, the latter will be removed). Therefore, it is very useful when you want to expose the data allocated in Rust as C strings.

Unfortunately, C strings always end with zero bytes and cannot contain a string in them, while Rust&[u8]/Vec is the exact opposite-they do not end with zero bytes, and any number of them can be contained in them This means starting from Vec. To CString has neither error nor no allocation-the CString constructor all checks for zeros in the data you provide, returns an error if it finds some, and appends one to the end of the byte vector Zero bytes, which may need to be reallocated.

Similar to String that implements Deref, CString implements Deref, so you can directly call the defined on CStr on CString Method. This is important because the as_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. String, & str, Vec and & [u8] are constructorsA valid parameter of CString::new(). Of course, if you pass a byte slice or a string slice, a new allocation will be created, and Vec or the string will be consumed.

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),
}
}

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 call CString::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 and the latter will be deleted). So, when you want to It is very useful when the data allocated in Rust is exposed as a C string.

Unfortunately, C strings always end with zero bytes and cannot contain a string in it, and Rust& ( u8]/Vec is the exact opposite-they do not end with zero bytes, and any number of them can be contained in them. This means starting from Vec. There is neither error nor no allocation to CString-CString construction The function checks the zeros in the data you provide, returns an error if some are found, and appends a zero byte to the end of the byte vector, which may require reallocation.

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 Vec. String, &str, Vec and & [u8] is a valid parameter of the constructor CString::new(). Of course, if you pass a byte slice or string slice, a new allocation will be created, and Vec or string will Is consumed.

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.

Leave a Comment

Your email address will not be published.