I have C++ code that calls Rust code with data. It knows which object to send the data to. Here's an example of the Rust function that C++ calls back:
extern "C" fn on_open_vpn_receive(
instance: Box<OpenVpn>,
data: *mut c_uchar,
size: *mut size_t,
) -> u8
It receives the pointer as a Box, so I created a function openvpn_set_rust_parent that sets which object the C++ must call back. This object is a pointer to itself. I'm using Pin so the Box is not reallocated to somewhere else, making C++ call an invalid address.
impl OpenVpn {
pub fn new() -> Pin<Box<OpenVpn>> {
let instance = unsafe { interface::openvpn_new(profile.as_ptr()) };
let o = OpenVpn { instance: instance };
let p = Box::pin(o);
unsafe {
interface::openvpn_set_rust_parent(o.instance, p.as_ptr());
};
p
}
}
Signature:
pub fn openvpn_set_rust_parent(instance: *mut OpenVpnInstance, parent: *mut OpenVpn)
I don't know how to transform p into *mut OpenVpn to pass to C++. Is my idea ok? I think the usage of Pin is good here, and I think this is a good way of calling the object from C++.
It doesn't matter.
Pinisn't a deeply magical type that forces your value to never move. Really, it boils down to strongly-worded documentation and some guide rails that prevents you from doing bad things within safe Rust code.Pincan be circumvented by unsafe code, which includes any FFI code.Having the
Pininside your Rust code might help you keep the Rust code accurate and valid, but it has nothing useful to add for the purposes of calling Rust from other languages.Pinis defined asrepr(transparent), which means that you can use it in your FFI signature as long as the inner type is safe to use in FFI:Pindoesn't do this,Boxdoes this. When you box something, you move the value to the heap. TheBoxitself is just a pointer. The address of the pointer will move around, but the address of the data in the heap will not.Note that the second address (
0x55..30, on the heap) printed is the same, even though theBoxitself has moved:See also: