我正在寻找一种方法,以确保结构寿命超过赋予该结构方法的参数。即使该结构在离开方法后不保留对该数据的引用。
这是用于将原始指针包装到FFI的。我想保证实现FFI的结构的寿命超过了我用来将Rust对象馈送到指针包装程序的Option<&'a Any>
。
Context
是FFI包装器。Data
具有映射到FFI类型的不同类型。 FFI函数会在返回之前立即复制所有这些类型。
除了原始指针。
所以我将寿命指定符添加到Context
中,并在send_data()
中使用它。
但是以某种方式,这还不够。我希望下面的代码无法编译。
编辑:Rust Discord建议有人在&self
中使mut
send_data()
可用。这具有理想的效果,但是我的FFI是线程安全的(无状态),并且send_data()
是时间紧迫的。因此,我非常想避免这种情况。
use std::any::Any;
use std::marker::PhantomData;
struct IntegerArray<'a> {
data: &'a [i32],
}
struct WrappedRawPointer<'a> {
ptr: *const std::ffi::c_void,
_marker: PhantomData<&'a ()>,
}
impl<'a> WrappedRawPointer<'a> {
fn new(data: Option<&'a dyn Any>) -> Self {
Self {
ptr: data
.map(|p| p as *const _ as *const std::ffi::c_void)
.unwrap_or(std::ptr::null()),
_marker: PhantomData,
}
}
}
enum Data<'a, 'b> {
IntegerArray(IntegerArray<'a>),
WrappedRawPointer(WrappedRawPointer<'b>),
}
struct Context<'a> {
ctx: u32,
_marker: PhantomData<&'a ()>,
}
impl<'a> Context<'a> {
fn new() -> Self {
Self {
ctx: 0, // Call FFI to initialize context
_marker: PhantomData,
}
}
fn send_data(&self, data: Data<'_, 'a>) {
match data {
Data::IntegerArray(_i) => (), // Call FFI function
Data::WrappedRawPointer(_p) => (), // Call FFI function
}
}
}
fn main() {
let ctx = Context::new();
{
let some_float: f32 = 42.0;
ctx.send_data(
Data::WrappedRawPointer(
WrappedRawPointer::new(
Some(&some_float)
)
)
);
// I would like rustc to complain
// here that some_float does not
// outlive ctx
}
// Explicitly drop outside
// the previous block to
// prevent rustc from being
// clever
drop(ctx);
}
使send_data
取&mut self
而不是&self
是可行的,因为相对于类型self
,它使Self
参数invariant的类型。 Subtyping and Variance在Rustonomicon中进行了描述,以及此处有关堆栈溢出的其他问题(请参见下文)。
因为即使在self
是不可变的引用时也要保持不变,所以这表明Context<'a>
itself]的方差是错误的:它在'a
中是协变的,但它应该是不变的。您可以通过将PhantomData
的类型参数更改为在'a
中也不变的内容来解决此问题:struct Context<'a> {
ctx: u32,
_marker: PhantomData<*mut &'a ()>, // or Cell<&'a ()>, or fn(&'a ()) -> &'a (), etc.
}
PhantomData
不仅仅是您机械地添加的内容,以使编译器不会对您大吼大叫。 PhantomData
的类型参数的特定形式告诉编译器如何
Context<'some_long_lifetime>
cant可以代替Context<'a_much_shorter_lifetime>
,即使其所有字段都允许该替换。Context
应该为Send
或Sync
,可能是相关的]