2个稳定版本

2.0.1 2023年7月9日
1.0.3 2022年1月4日

1349数据结构 中排名

每月29次下载

MIT OR Apache-2.0 OR CC-BY-4.0

11KB

Phtm

Crates.io Crates.io docs.rs

此crate提供对PhantomData常见使用的类型别名。

[dependencies]
phtm = "2.0.1"

文档


lib.rs:

PhantomData相关的各种详细简写类型。

变异性

变异性对于初学者来说可能非常难以理解。通常,当在Rust中谈论“子类型”和“超类型”时,它特别指的是当前版本的生存期。

具体来说,任何引用类型 &'any T 是静态引用类型 &'static T 的子类型。具有较短的生存期的引用是具有较长的生存期的引用的子类型。

协变

Foo<T>T上“协变”时,它共享T相同的子类型规则,即Foo<&'a T>Foo<&'static T>的子类型。当生存期参数是协变的,它表明较短的生存期参数是较长的生存期参数的子类型,即Foo<'a>Foo<'static>的子类型。

逆变

协变较为罕见,它 反转了正常的子类型规则。 Foo<'static>Foo<'a> 的子类型,如果生命周期是协变的。这是函数参数类型的属性。 fn(&'static ()) 是最严格的,将成为所有 fn(&'a ()) 的超类型。

不变性

如果参数是不变的,则不能改变它。只有相等的参数是其自身的子/超类型。在 &mut T 中,T 是不变的,因为你可以对其进行读写操作,你不能使用比 T 更短生命周期的 T2 来写入,也不能读取比 T 更长生命周期的 T3。由于类似的原因,在 fn(T) -> T 中,T 也是不变的。

丢弃检查

这个crate提供的 CovariantOver 类型与 Owns 类型的区别在于,Owns 将导致编译器使用 "丢弃检查"。丢弃检查防止在另一个类型的析构函数中使用已丢弃的值。以下示例取自并改编自 相应的 Rustonomicon 章节a

想象一个自定义的 Box 类型,定义如下

use std::ptr::NonNull;
struct MyBox<T> {
    inner: NonNull<T>,
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) { /* free memory.. */ }
}

虽然 NonNullT 上是协变的,但 MyBox 必须使用 PhantomData 来表示它拥有 T,否则它可能会允许在析构函数中访问已丢弃的数据

struct Inspector<'a>(&'a u8);
impl<'a> Drop for Inspector<'a> {
   fn drop(&mut self) {
       println!("I was only {} days from retirement!", self.0);
   }
}
struct World<'a> {
    inspector: Option<MyBox<Inspector<'a>>>,
    days: Box<u8>,
}

fn main() {
    let mut world = World {
        inspector: None,
        days: Box::new(1),
    };
    world.inspector = Some(MyBox::new(Inspector(&world.days)));
    // Let's say `days` happens to get dropped first.
    // Then when Inspector is dropped, it will try to read free'd memory!
}

在这个例子中,使用 PhantomData<T>Owns<T> 标记 MyBox 可以解决不稳定性问题

struct MyBox<T> {
    inner: NonNull<T>,
    _owns_t: Owns<T>,
}

fn main() {
    let mut world = World {
        inspector: None,
        days: Box::new(1),
    };

    world.inspector = Some(MyBox::new(Inspector(&world.days)));
    // ^ now fails to compile!
}

标记

可以通过添加 NotSendOrSyncNotSync 到你的类型,以及/或者为你的类型编写 unsafe impl 标记来控制 SendSync 标记。标记控制哪些类型可以在线程之间安全地发送。非原子的引用计数类型 Rc 不能在线程之间发送,因为可能有两个线程持有相同的对象,这可能会非原子地增加引用计数,导致数据竞争。

通常情况下,当存在指针字段或单线程容器(如 Rc)时,您的类型会自动不是 Send,但明确地将类型定义为不是 Send 或不是 Sync 也可能带来好处。

这样做可以防止在公共类型突然停止实现这些标记类型时出现semver风险,这可能导致破坏性变更,而明确添加标记类型则允许在未来具有单线程容器,而不必提升主版本号。

无运行时依赖