9

我发现PhantomDataRust 中的概念相当混乱。我在基于 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{}or PhantomData()

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

  3. 在我的MyStruct::new()方法的声明中,如果我忘记显式指定参数的'a生命周期context,那么 的魔力PhantomData就会消失,并且可以丢弃Contextbefore MyStruct。这是相当阴险的;编译器甚至没有给出警告。那么它分配给什么生命周期marker,为什么?

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

4

1 回答 1

6

marker: PhantomData从语法上讲,这到底是什么东西?我的意思是,它看起来不像构造函数,我希望它类似于PhantomData{}or PhantomData()

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

struct Foo;

并像这样创建它的一个实例:

let foo: Foo = Foo;

类型和值都被命名Foo

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

没有什么特别的,PhantomData只是它的类型参数未使用不是错误(参见源代码)。使用属性启用此行为,该#[lang = "phantom_data"]属性只是为此目的在编译器中的一个钩子。

在我的MyStruct::new()方法的声明中,如果我忘记显式指定参数的'a生命周期context,那么 的魔力PhantomData就会消失,并且可以丢弃Contextbefore MyStruct。这是相当阴险的;编译器甚至没有给出警告。那么它分配给什么生命周期marker,为什么?

PhantomData是否可以让您告诉编译器信息它无法自行推断,因为该信息是关于您不直接使用的类型的。由您为编译器提供正确的信息。

在我的MyStruct::new()方法的声明中,如果我忘记显式指定参数的'a生命周期context,那么 的魔力PhantomData就会消失,并且可以丢弃Contextbefore MyStruct。这是相当阴险的;编译器甚至没有给出警告。那么它分配给什么生命周期marker,为什么?

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

于 2020-04-19T17:37:12.280 回答