Closed tmarkovski closed 4 years ago
That's not really the intended usage of FfiStr — it's essentially for const inputs. What's the i32 for, an error code? That might be a better input parameter, and then you could return a *const c_char
for the result (call_with_result
or call_with_output
can help here, and will take care of panic handling too — note that panicing in an extern "C"
function is UB in rust unless you enable panic="abort").
In truth, that's not really the intended usage of ByteBuffer either, which is usually intended for return values. That said, it should be fine to use it this way, there just aren't a lot of docs or guardrails on it.
I don't work on this anymore, but @eoger or @rfk probably can help point you in the right direction.
Thanks for your response @thomcc
I'm curious about your remark that ByteBuffer
should be uses as a return value. We've been using this crate quite a bit in this library and built FFI wrappers in C#, Java and ObjC. Do you see any issues with the way we use ByteBuffer
both as input and output values? We make sure to expose macro define_bytebuffer_destructor
which should be called for all values passed back to the consumer via &mut ByteBuffer
.
How do I safely pass back the result into fingerprint?
You may find the rust_string_to_c
helper useful here, although it returns a *mut c_char
rather than the FfiStr
wrapper. As Thom said, you probably don't want FfiStr
for this anyway.
Do you see any issues with the way we use ByteBuffer both as input and output values?
Naively, that looks fine to me, although it took me a few moments to understand how you were freeing the resulting ByteBuffer
.
I'd be tempted to use entirely different types for "things owned by rust" and "things owned by the foreign-language code" though, just to make it easier to keep track of which things need to be freed how. That function also seems to be doubling-up on error codes, using both an &mut ExternError
that can signal success or failure and returning an integer response code. But that's entirely a style thing :-)
At the risk of a bit of shameless cross-promotion, you may also be interested in a related project we've been working to to make some higher-level abstractions for generating foreign-language bindings to Rust code, using ffi-support
as a building block: https://github.com/mozilla/uniffi-rs/.
It's probably not ready enough for your use-case just yet, but I figured I'd share it out of interest.
Naively, that looks fine to me, although it took me a few moments to understand how you were freeing the resulting
ByteBuffer
.
Yeah, the callers are required to free both ByteBuffer
and the string pointer in ExternErr
in case of an error. In case of C#, we utilize the IDisposable
to free up any rust allocated resources. It's not ideal, but implementers can take care of it by not exposing the FFI methods directly.
At the risk of a bit of shameless cross-promotion, you may also be interested in a related project we've been working to to make some higher-level abstractions for generating foreign-language bindings to Rust code, using
ffi-support
as a building block: https://github.com/mozilla/uniffi-rs/.It's probably not ready enough for your use-case just yet, but I figured I'd share it out of interest.
This is something we're highly interested in, thank you for sharing it. We're currently using Protobuf+FFI to ensure consistent cross platform data interface, but uniffi looks a lot more idiomatic with UDL.
I have a method signature like this
Is this correct use of
FfiStr
? How do I safely pass back the result into fingerprint? I'm somewhat new to Rust.