当让 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?
我有使用数据调用 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 theBox
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?