18 个版本

0.1.17 2024 年 1 月 2 日
0.1.16 2023 年 11 月 1 日
0.1.14 2023 年 7 月 21 日
0.1.9 2023 年 3 月 18 日
0.1.0 2018 年 12 月 23 日

#380Rust 模式

Download history 25277/week @ 2024-04-08 30960/week @ 2024-04-15 35349/week @ 2024-04-22 29222/week @ 2024-04-29 32106/week @ 2024-05-06 28188/week @ 2024-05-13 43160/week @ 2024-05-20 33138/week @ 2024-05-27 31620/week @ 2024-06-03 33983/week @ 2024-06-10 29134/week @ 2024-06-17 36023/week @ 2024-06-24 27498/week @ 2024-07-01 31788/week @ 2024-07-08 28556/week @ 2024-07-15 26556/week @ 2024-07-22

115,895 每月下载量
用于 324 个 crate(直接使用 8 个)

MIT/Apache 许可

31KB
430

定义您自己的 PhantomData

github crates.io docs.rs build status

此 crate 允许您定义自己的 PhantomData 以及具有泛型参数的类似行为的单位类型,这在普通的 Rust 中是不允许的。

[dependencies]
ghost = "0.1"

支持 rustc 1.31+

背景

PhantomData 是 Rust 标准库中定义的,它在 Rust 代码中无法定义相同的类型,这是神奇的。它在标准库中这样定义

#[lang = "phantom_data"]
pub struct PhantomData<T: ?Sized>;

#[lang = "..."] 属性表示这是一个 lang item,一个编译器所知的特殊情况。它是唯一允许携带未使用类型参数的类型。

如果我们尝试使用类型参数定义等效的单位结构体,编译器将拒绝它。

struct MyPhantom<T: ?Sized>;
error[E0392]: parameter `T` is never used
 --> src/main.rs:1:18
  |
1 | struct MyPhantom<T: ?Sized>;
  |                  ^ unused type parameter
  |
  = help: consider removing `T` or using a marker such as `std::marker::PhantomData`

此 crate 提供了一个 #[phantom] 属性,它使得定义具有泛型参数的单位结构体成为可能。

示例

use ghost::phantom;

#[phantom]
struct MyPhantom<T: ?Sized>;

fn main() {
    // Proof that MyPhantom behaves like PhantomData.
    let _: MyPhantom<u8> = MyPhantom::<u8>;
    assert_eq!(0, std::mem::size_of::<MyPhantom<u8>>());
}

// Proof that MyPhantom is not just a re-export of PhantomData.
// If it were a re-export, these would be conflicting impls.
trait Trait {}
impl<T> Trait for std::marker::PhantomData<T> {}
impl<T> Trait for MyPhantom<T> {}

// Proof that MyPhantom is local to the current crate.
impl<T> MyPhantom<T> {
}

实现接受 where 子句、生命周期、多个泛型参数和 derives。以下是一个虚构的调用,它演示了所有内容

use ghost::phantom;

#[phantom]
#[derive(Copy, Clone, Default, Hash, PartialOrd, Ord, PartialEq, Eq, Debug)]
struct Crazy<'a, V: 'a, T> where &'a V: IntoIterator<Item = T>;

fn main() {
    let _ = Crazy::<'static, Vec<String>, &'static String>;

    // Lifetime elision.
    let crazy = Crazy::<Vec<String>, &String>;
    println!("{:?}", crazy);
}

方差

#[phantom] 属性接受对单个泛型参数(既包括生命周期参数也包括类型参数)的属性,以使它们为逆变或不变。默认为协变。

  • #[contra] — 逆变泛型参数
  • #[invariant] — 不变泛型参数

方差的影响在 Rustonomicon 的 子类型章节 中有更详细的解释。

use ghost::phantom;

#[phantom]
struct ContravariantLifetime<#[contra] 'a>;

fn f<'a>(arg: ContravariantLifetime<'a>) -> ContravariantLifetime<'static> {
    // This coercion is only legal because the lifetime parameter is
    // contravariant. If it were covariant (the default) or invariant,
    // this would not compile.
    arg
}

#[phantom]
struct Demo<A, #[contra] B, #[invariant] C>;

文档

处理公开的幻影类型的Rustdoc文档有两种选择。

您可以直接在明显的幻影结构体上提供文档,但Rustdoc会随意显示由#[phantom]宏发出的机制实现细节,这可能会分散注意力。如果您需要记录任何公开的方法,建议采用这种方式,因为方法在另一种选择中是不可见的。

use ghost::phantom;

/// Documentation.
#[phantom]
pub struct MyPhantom<T: ?Sized>;

impl<T: ?Sized> MyPhantom<T> {
    /// Documentation on methods.
    pub fn foo() {}
}

如果您没有添加方法或者不需要在文档中渲染方法,建议的做法如下。Rustdoc将显示一个不太分散的类型签名和所有的特质实现,但不会显示固有方法。

mod private {
    use ghost::phantom;

    #[phantom]
    pub struct MyPhantom<T: ?Sized>;
}

/// Documentation goes here.
#[allow(type_alias_bounds)]
pub type MyPhantom<T: ?Sized> = private::MyPhantom<T>;

#[doc(hidden)]
pub use self::private::*;

使用场景

完全取决于您的想象。比如,一个支持以下语法的类型注册库如何

for flag in Registry::<Flag> {
    /* ... */
}

许可证

可以在Apache许可证,版本2.0MIT许可证中选择一种。
除非您明确声明,否则,根据Apache-2.0许可证定义,您提交的任何有意包含在此软件包中的贡献都将如上所述双重许可,不附加任何额外条款或条件。

依赖项

~280–730KB
~17K SLoC