在Rust中,我发现PhantomData
的概念非常令人困惑。我广泛地使用它来约束我的基于FFI的代码中的对象生命周期,但我仍然不确定我是否正确地使用了它。
这是一个编造的示例,展示我通常如何使用它。例如,我不希望MyStruct
的实例超过Context
的实例存在:
// FFI declarations and types
mod ffi {
use std::ffi::c_void;
pub type handle_t = *const c_void;
// ...
}
// A wrapper structure for some context created and maintained
// inside the C library
struct Context {
// ...
}
// Handle is only valid as long as the Context is alive.
// Hence, I use the PhantomData marker to constrain its lifetime.
struct MyStruct<'a> {
marker: PhantomData<&'a Context>,
handle: ffi::handle_t,
}
impl<'a> MyStruct<'a> {
fn new(context: &'a Context) -> Self {
let handle: ffi::handle_t = context.new_handle();
MyStruct {
marker: PhantomData,
handle
}
}
}
fn main() {
// Initialize the context somewhere inside the C library
let ctx = Context::new(unsafe {ffi::create_context()});
// Create an instance of MyStruct
let my_struct = MyStruct::new(&ctx);
// ...
}
我不太明白以下内容:
marker: PhantomData
这个东西到底是什么,从语法上来看?我的意思是,它看起来不像一个构造器,我期望的应该是像PhantomData{}
或者PhantomData()
这样的形式。为了进行生命周期跟踪,
PhantomData
是否真的在乎marker
声明中的实际类型?我尝试将其更改为PhantomData<&'a usize>
,但它仍然可以工作。在我的
MyStruct::new()
方法的声明中,如果我忘记显式地指定'a
生命周期的context
参数,那么PhantomData
的魔力就会消失,并且在MyStruct
之前释放Context
就变得可以了。这非常隐蔽; 编译器甚至没有发出警告。它给marker
分配了什么生命周期,为什么?与先前的问题相关;如果有多个输入参考参数具有潜在不同的生命周期,
PhantomData
如何确定要使用哪个生命周期?
PhantomData
的实际含义存在一个严重的误解,所以我把它们都放在了一个问题里。 - Roman Dmitrienko