struct MyStruct{
pub field: u8
}
impl MyStruct{
pub fn do_something(&mut self, value: u8) {
self.field += value;
}
}
#[no_mangle]
pub extern "C" fn do_something_callback(
my_struct: ________,
value: u8,
)
fn main() {
let my_struct = MyStruct{field: 0};
let cpp_object = create_cpp_object();
//tells C++ object to call my_struct when it has data
set_callback_in_cpp(cpp_object, my_struct);
}
and then I want to call C++ from Rust and specify that C++ calls a callback on Rust that calls a 'method' on MyStruct.
This is not like this approach: FFI - The Rustonomicon where I create an opaque struct to represent the Rust object. In this case it's a non opaque object.
How can do_something_callback's signature be so C++ can call it back on an instance of MyStruct?
Then you use the opaque object pattern, by Box-ing your object on the Rust side:
// In C / C++:
#ifdef __cplusplus
extern "C" {
#endif
+ typedef struct VecString VecString_t; // opaque object
typedef struct {
- uint8_t field;
+ VecString_t * vec_string; // pointer to it.
} MyStruct;
and in Rust:
#[repr(C)]
pub
struct MyStruct {
- pub field: u8,
+ /// A type with the same ABI as a pointer, such as `Option<Box< _ >>`
+ pub vec_string: Option<Box< Vec<String> >>,
}
you care about representing the struct in the C++ side, but it's not necessary for me. In that case, can't I use a struct normally in Rust with any members and then make the C++ code call a Box<MyStruct>? I just care about MyStruct on the Rust side, I only need to pass to C++ a reference/pointer to it so Rust knows for who to deliver the call when C++ calls
You originally said you wanted a non-opaque type. The opaque type in @Yandros's most recent post is struct VecString, aka VecString_t. MyStruct is still non-opaque.
It does not seem to me entirely clear why you need to wrap *mut Vec<String> in a repr(C) struct. Technically the C standard allows pointers to different types to have different sizes and layouts, but that is true not just in function parameters but also inside structs, so the idea that wrapping a(n apparently) non-FFI-safe pointer in a repr(C) struct makes it FFI-safe seems wrong to me. Perhaps I am missing something...
The starting point of this thread, from where I've derived my examples, was using a MyStruct type by value, which is necessarily an FFI-safe / non-opaque type. Hence my first suggestion.
After the mention of there being non-FFI-friendly types, such as Vec<String>, I've shown how with (slim) pointer indirection one can make anything FFI-safe, and use that as the field of a well-defined struct: it's not because one part is opaque that everything must be opaque.
Obviously, using MyStruct as a defined new type is not mandatory here.
For the sake of the example, one could also do typedef VecString_t * MyStruct;, i.e., replace every occurence of MyStruct in the previous example by a VecString_t *.
You can even just use void * everywhere in the FFI API, if you want to sacrifice type-safety in exchange of having a fixed / Rust-agnostic header.
By including a private field and no constructor, we create an opaque type that we can't instantiate outside of this module. (A struct with no field could be instantiated by anyone.) We also want to use this type in FFI, so we have to add #[repr(C)]. And to avoid warning around using () in FFI, we instead use an empty array, which works just as well as an empty type but is FFI-compatible.
I.e. even though the type is opaque and not exotically sized, #[repr(C)] is still required, even just to use it by pointer.
Let's say the nomicon phrases stuff in a slightly ambiguous manner: slim pointers are guaranteed to "be FFI-safe", if:
either the pointee is FFI-safe too (in which case the pointer may be dereferenced by the FFI side, provided non-nullness, alignment, and other safety invariants of the actual pointer instance are met).
or the pointee is opaque FFI-wise: indeed, in that case the pointer is not really one, it's kind of a usize-like handle that is used by feeding it into other FFI-exported functions.