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

Should I use Pin when making C++ call a Rust method through a pointer?

我有使用数据调用 Rust 代码的 C++ 代码。它知道将数据发送到哪个对象。这是 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 不是那种强迫你的价值永远不动的很神奇的类型。实际上,它归结为措辞强硬的文档和一些指南 rails 可以防止您在安全的 Rust 代码 中做坏事 Pin 可以被不安全代码规避,其中包括任何 FFI 代码。

在您的 Rust 代码中加入 Pin 可能有助于您保持 Rust 代码的准确性和有效性,但对于从其他语言调用 Rust 而言,添加它没有任何用处。

Pin is defined as 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,
}

I'm using Pin so the Box is not reallocated to somewhere else, making C++ call an invalid address.

Pin 不这样做,Box 这样做。当您装箱某些东西时,您将值移动到堆中。 Box 本身只是一个指针。指针的地址会移动,但是数据在堆的地址不会移动

请注意,尽管 Box 本身已移动,但打印的第二个地址(0x55..30,在堆上)是相同的:

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
}

另请参阅:

  • What are the use cases of the newly proposed Pin type?