I am trying to use rust to interface with an ancient platform that expects the functions to be declared and externed in a specific C-Style format.
char *func(int argc, char *argv[])
I am trying to reproduce this signature in Rust. I need to be able to properly parse arguments (ideally as Rust Strings) and then return an arbitrary value as a string without disrupting the signature.
So far, I have the following:
#[no_mangle]
pub extern "C" fn my_function(argc: isize, argv: *const c_char) -> (CString) {
let output: CString = CString::new("Test Output").expect("CString::new failed");
return output;
}
Which properly returns my test string through the interface, but I can't figure out how to parse argv into a usable format, or if my implementation of argv is even properly compliant with the specified format. I am not familiar with pointers, and I am not even sure where to start with figuring this out.
With your existing code, you have a few primary issues:
argv: *const c_char
is wrong because the original C code is an array of pointers, not a pointer to a char. The correct type is
argv: *const *const c_char
You cannot return CString
because that is not a type that C will know what to do with. You need to return *mut c_char
just like the C function did. To do this, you use output.into_raw()
to unwrap the string and get the underlying *mut c_char
pointer. This also means that you are giving this C code a random pointer, and it can read it as a C string, but it can't free that memory, so you're either leaking that memory, or you need a second function to pass the string back to Rust to be freed later using from_raw
. In this case I'm using *mut c_char
because that's what into_raw
returns.
You should consistently use the c_
types for everything in the C interface, including argc
, e.g.
my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char
To process the args, you want to loop through the argc
values to wrap each pointer into a CStr
, which you can then work with without needing to worry about unsafe code.
All taken together:
#[no_mangle]
pub extern "C" fn my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char {
let argv: Vec<_> = (0..argc)
.map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
.collect();
// Whatever processing you need to do.
let output = CString::new("Test Output").expect("CString::new failed");
return output.into_raw();
}
and potentially
#[no_mangle]
pub extern "C" fn my_function_free(s: *mut c_char) {
unsafe {
CString::from_raw(s);
}
}