我有用数据调用Rust代码的C++代码。它知道将数据发送到哪个对象
extern "C" fn on_open_vpn_receive(
instance: Box<OpenVpn>,
data: *mut c_uchar,
size: *mut size_t,
) -> u8
它以Box
的形式接收指针,因此我创建了一个函数openvpn_set_rust_parent
,用于设置C++必须回调的对象。此对象是指向其自身的指针。我使用的是Pin
,所以Box
不会被重新分配到其他地方,从而使C++调用成为无效地址。
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
}
}
签名:
pub fn openvpn_set_rust_parent(instance: *mut OpenVpnInstance, parent: *mut OpenVpn)
我不知道如何将p
转换为*mut OpenVpn
以传递给C++。我的想法可以吗?我认为Pin
在这里的用法很好,我认为这是从C++调用对象的好方法。
这并不重要。Pin
并不是一个非常神奇的类型,它迫使你的价值永远不会改变。实际上,它可以归结为措辞强硬的文档和一些指南,防止您在安全的Rust代码中做坏事。Pin
可以通过不安全代码规避,其中包括任何FFI代码。
在Rust代码中包含Pin
可能有助于保持Rust代码的准确性和有效性,但对于从其他语言调用Rust来说,它没有任何用处。
Pin
定义为repr(transparent)
,这意味着您可以在FFI签名中使用它,只要内部类型在FFI:中使用是安全的
#[stable(feature = "pin", since = "1.33.0")]
#[lang = "pin"]
#[fundamental]
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct Pin<P> {
pointer: P,
}
我使用的是
Pin
,因此Box
不会被重新分配到其他地方,从而使C++调用成为无效地址。
Pin
不执行此操作,Box
执行此操作。当你把某个东西装箱时,你会把值移到堆里。Box
本身只是一个指针。指针的地址会移动,但堆中数据的位置不会移动。
请注意,打印的第二个地址(堆上的0x55..30
(是相同的,即使Box
本身已经移动:
fn main() {
let a = 42;
let b = Box::new(a);
println!("{:p}", &b); // 0x7ffe3598ea80
println!("{:p}", &*b); // 0x556d21528b30
let c = b;
println!("{:p}", &c); // 0x7ffe3598ea88
println!("{:p}", &*c); // 0x556d21528b30
}
另请参阅:
- 新提出的Pin类型有哪些用例
- 如何将Pin结构与自引用结构一起使用