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 日 |
#380 在 Rust 模式 中
115,895 每月下载量
用于 324 个 crate(直接使用 8 个)
31KB
430 行
定义您自己的 PhantomData
此 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.0或MIT许可证中选择一种。除非您明确声明,否则,根据Apache-2.0许可证定义,您提交的任何有意包含在此软件包中的贡献都将如上所述双重许可,不附加任何额外条款或条件。
依赖项
~280–730KB
~17K SLoC