PhantomData 在 Rust 中究竟是如何工作的?

How exactly does PhantomData work in Rust?

我发现 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);

    // ...
}

我不太明白以下内容:

  1. marker: PhantomData 这个东西在句法上到底是什么?我的意思是,它看起来不像构造函数,我希望它像 PhantomData{}PhantomData().

  2. 出于跟踪生命周期的目的,PhantomData 甚至关心 marker 声明中的实际类型吗?我尝试将其更改为 PhantomData<&'a usize>,但它仍然有效。

  3. 在我的 MyStruct::new() 方法的声明中,如果我忘记明确指定 context 参数的 'a 生命周期,[=11 的魔法=] 消失,可以在 MyStruct 之前删除 Context。这是相当阴险的;编译器甚至不发出警告。那么它分配给 marker 的生命周期是多少,为什么?

  4. 与上一个问题相关;如果有多个可能具有不同生命周期的输入引用参数,PhantomData 如何确定使用哪个生命周期?

What exactly is this marker: PhantomData thing, syntactically? I mean, it does not look like a constructor, which I'd expect to be something like PhantomData{} or PhantomData().

您可以定义一个零字段结构,如下所示:

struct Foo;

并像这样创建一个实例:

let foo: Foo = Foo;

类型和值都命名为Foo

For the purposes of lifetime tracking, does PhantomData even care about the actual type in the declaration of marker? I tried changing it to PhantomData<&'a usize>, and it still worked.

PhantomData 没有什么特别之处,只是它的类型参数未被使用不是错误(参见 the source)。此行为是通过 #[lang = "phantom_data"] 属性启用的,它只是编译器中为此目的的一个挂钩。

In the declaration of my MyStruct::new() method, if I forget to explicitly specify the 'a lifetime for the context argument, the magic of PhantomData disappears, and it becomes OK to drop Context before MyStruct. This is quite insidious; the compiler does not even give a warning. What lifetime does it assign to marker then, and why?

PhantomData 是为了让你告诉编译器它不能推断自己的信息,因为这个信息是关于你没有直接使用的类型的。给编译器正确的信息取决于你。

In the declaration of my MyStruct::new() method, if I forget to explicitly specify the 'a lifetime for the context argument, the magic of PhantomData disappears, and it becomes OK to drop Context before MyStruct. This is quite insidious; the compiler does not even give a warning. What lifetime does it assign to marker then, and why?

我不太确定我是否理解这个问题。 PhantomData 不会 任何事情 - 它只是一种与编译器沟通的方式,表明您正在以某种方式使用数据,您可以准确地表达该信息。请注意,即使您错误地表达了约束,也只有在您也有 unsafe 代码的情况下才可能引入内存不安全。在 PhantomData 中正确表达生命周期是围绕不安全代码创建安全抽象的一部分。