【问题标题】:Why would I create a struct with only a `PhantomData<()>` member?为什么我要创建一个只有 `PhantomData<()>` 成员的结构?
【发布时间】:2016-01-05 11:15:49
【问题描述】:

在阅读 the answer for another question 时,我看到了这个结构 [1]:

struct Clipboard {
    marker: PhantomData<()>,
}

虽然我看到了PhantomData 的其他用法,但它们都被参数化为一个有趣的类型,例如PhantomData&lt;&amp;'a [u8]&gt;PhantomData&lt;T&gt;。为什么要创建 作用 的结构,就好像它包含一个空元组一样?

[1]:我实际上另一个答案时有点诗意,但被要求解释为什么我会这样做。评论太长了。

【问题讨论】:

    标签: rust


    【解决方案1】:

    在这种情况下,剪贴板是一个全局共享资源,当您打开它时不会给您任何令牌。在不先打开剪贴板的情况下尝试使用剪贴板将是一件坏事。如果你做了简单的事情并创建了一个空结构,那么你可以忘记调用正确的构造方法:

    struct Clipboard;
    
    impl Clipboard {
        fn new() -> Clipboard {
            println!("Clipboard opened");
            Clipboard
        }
    
        fn copy(&self) -> String { "copied".into() }
    }
    
    let c = Clipboard::new(); // Correct
    println!("{}", c.copy());
    let c = Clipboard;        // Nope, didn't open the clipboard properly
    println!("{}", c.copy()); // But can still call methods!?!?!
    

    让我们尝试一个内部带有虚拟值的元组结构:

    struct ClipboardWithDummyTuple(());
    
    impl ClipboardWithDummyTuple {
        fn new() -> ClipboardWithDummyTuple {
            println!("Clipboard opened");
            ClipboardWithDummyTuple(())
        }
    
        fn copy(&self) -> String { "copied".into() }
    }
    
    let c = ClipboardWithDummyTuple::new(); // Correct
    println!("{}", c.copy());
    let c = ClipboardWithDummyTuple;
    println!("{}", c.copy()); // Fails here
    // But because `c` is a method, not an instance              
    

    这样更好,但错误发生的时间比我们希望的要晚;它仅在我们尝试使用剪贴板时发生,而不是在我们尝试构建它时发生。让我们尝试使用命名字段创建结构:

    struct ClipboardWithDummyStruct { 
        // warning: struct field is never used
        dummy: (),
    }
    
    impl ClipboardWithDummyStruct {
        fn new() -> ClipboardWithDummyStruct {
            println!("Clipboard opened");
            ClipboardWithDummyStruct { dummy: () }
        }
    
        fn copy(&self) -> String { "copied".into() }
    }
    
    let c = ClipboardWithDummyStruct::new(); // Correct
    println!("{}", c.copy());
    let c = ClipboardWithDummyStruct; // Fails here
    // But we have an "unused field" warning
    

    所以,我们更接近了,但这会产生关于未使用字段的警告。我们可以使用 #[allow(dead_code)] 关闭该字段的警告,或者我们可以将该字段重命名为 _dummy,但我相信有更好的解决方案 — PhantomData

    use std::marker::PhantomData;
    
    struct ClipboardWithPhantomData { 
        marker: PhantomData<()>,
    }
    
    impl ClipboardWithPhantomData {
        fn new() -> ClipboardWithPhantomData {
            println!("Clipboard opened");
            ClipboardWithPhantomData { marker: PhantomData }
        }
    
        fn copy(&self) -> String { "copied".into() }
    }
    
    let c = ClipboardWithPhantomData::new(); // Correct
    println!("{}", c.copy());
    
    let c = ClipboardWithPhantomData; // Fails here
    

    这不会产生任何警告,PhantomData 用于指示正​​在发生“不同”的事情。我可能会对结构定义发表一点评论,以说明我们为什么以这种方式使用PhantomData

    这里的很多想法都源于一个半相关的Rust issue about the correct type for an opaque struct

    【讨论】:

    • 我个人认为我更喜欢_dummy: ()
    • @ChrisMorgan 我很想听听更多!我倾向于PhantomData,因为我将其读为“这应该里面有一个Foo,但由于某种原因不能”。虽然如果它可以是PhantomData&lt;ClipboardAccessToken&gt; 或者更自我记录的东西,我会更开心。
    • 虽然它是PhantomData&lt;()&gt;,但它可以存储()。我倾向于“PhantomData 应该总是包含一个泛型类型”(例如PhantomData&lt;T&gt;PhantomData&lt;*const T&gt;&c.),尽管我没有检查使用情况或详细考虑过它。
    猜你喜欢
    • 2018-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    相关资源
    最近更新 更多