I've not done much rust so i've jumped off at the deep end to by doing some FFI C calls. I'm unsure of the correct way to make one of my calls.
I have a C function which has the signature something like void f(char** buf);
In C you can do these two calls.
char* buf;
f(&buf); // Function returns a pointer to something and does something
f(NULL); // Function does something but doesn't return a pointer.
What would be the correct signature for this function in rust? I hope I don't embarrass my self but I have this. fn f(buf : &*mut libc::c_void);?
How should I do the two different calls?
let mut buf : [i8; 5] = [1,2,3,4,5];
unsafe{f(& buf.as_mut_ptr());} // seems to work but I'm not entirely sure it is correct.
unsafe{f(& *ptr::null_mut());} // I get the warning 'this code causes undefined behavior when executed'
For the signature you gave: void f(char** buf);, the rust extern signature is a pretty mechanical translation, for pointers you can nearly just flip them around, you just need to explicitly say they are * mut (as opposed to * const)
So your example void f(char** buf); would translate to:
extern "C" {
fn f(buf: *mut *mut c_char);
}
This translation is so mechanical that you probably want to look into using bindgen for anything serious.
References are not "FFI-safe" (they don't have a guaranteed ABI on the platform like pointers do, at of you use them you should be getting a warning about it. As a rule of thumb, primitive number types like u32 and f64, anything in std::ffi or std::ptr, and your types with an explicit layout like #[repr(C)] are good.
Also remember to match any types: in C char, int etc might end up as different sizes or even signed-ness, so be sure use the equivalent in std::ffi - or use explicitly sized types on the C side.
It seems a bit silly as a general statement. There's are plenty of good crates that are bindings to an existing C library.
Perhaps they had some more specific meaning in that context? For example if the reason you're using Rust is because you want to make some existing C code safe and secure, adding a Rust wrapper does nothing to help and can easily make things worse.
You may be thinking of the fact that “wide” references to dynamically sized types do not have a guaranteed ABI — but this is equally true of raw pointers to such types.
In my opinion, you can and should use references in FFI function signatures when, if you didn't, the thing you'd be doing anyway is immediately converting to or from a reference. In that case, putting the reference in the signature is no more fragile, reduces the complexity of the program, and removes an opportunity for doing the conversion wrong.