2个稳定版本
2.0.1 | 2023年7月9日 |
---|---|
1.0.3 |
|
1349 在 数据结构 中排名
每月29次下载
11KB
Phtm
此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.. */ }
}
虽然 NonNull
在 T
上是协变的,但 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!
}
标记
可以通过添加 NotSendOrSync
或 NotSync
到你的类型,以及/或者为你的类型编写 unsafe impl
标记来控制 Send
和 Sync
标记。标记控制哪些类型可以在线程之间安全地发送。非原子的引用计数类型 Rc
不能在线程之间发送,因为可能有两个线程持有相同的对象,这可能会非原子地增加引用计数,导致数据竞争。
通常情况下,当存在指针字段或单线程容器(如 Rc
)时,您的类型会自动不是 Send
,但明确地将类型定义为不是 Send
或不是 Sync
也可能带来好处。
这样做可以防止在公共类型突然停止实现这些标记类型时出现semver风险,这可能导致破坏性变更,而明确添加标记类型则允许在未来具有单线程容器,而不必提升主版本号。