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

问题描述

我有C ++代码,该代码使用数据调用Rust代码。它知道将数据发送到哪个对象。这是C ++回调的Rust函数的示例:

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 is definedrepr(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
}

另请参阅: