当C++通过指针调用Rust方法时,我应该使用Pin吗



我有用数据调用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结构与自引用结构一起使用

最新更新