为什么std::rc::Rc需要PhantomData?

14
我知道PhantomData旨在消耗在数据类型定义中未被使用的生命周期或类型参数。最近我查看了Rust标准库Rc的定义,并注意到它似乎使用了PhantomData,但是在同级字段ptr中似乎使用了T作为 NonNull<RcBox<T>>。文档说NonNull"*mut T,但不为零且协变。"并进一步通过以下语句扩展了该定义:

*mut T不同,NonNull<T>在T上是协变的。如果这对你的用例不正确,则应在你的类型中包含一些PhantomData来提供不变性,例如PhantomData<Cell<T>>PhantomData<&'a mut T>

因此,这是为了方差还是更多地因为NonNull实际上是一个原始指针,并且需要使用PhantomData来消耗省略的生命周期,正如这个答案似乎暗示的那样

什么是省略的生命周期?没有需要省略的生命周期。 - trent
啊,对了,由于没有显式的生命周期、任何引用类型和裸指针的边界,我们没有提到生命周期,但这是否意味着PhantomData是为子类型化问题提供的?我引用的句子后面继续说道:“通常这不是必要的;协变对于大多数安全抽象是正确的,例如Box、Rc、Arc、Vec和LinkedList。”如果我理解正确,这意味着提供幻影类型使得容纳类型在T上不变,但是Rc使用它,这句话暗示它在T上是协变的? - rjs
3
Rc并不总是有PhantomData字段。这是引入它的提交: https://github.com/rust-lang/rust/commit/60dc10492ccdf785678d475172f2653aae9606da#diff-da456bd3af6d94a9693e625ff7303113R286 - Peter Hall
提交信息引用了这个对话,但那似乎很旧,也没有完全解释为什么要这样做。 - Peter Hall
2
我猜这与丢弃逻辑有关,而与生命周期或子类型无关。我认为@PeterHall链接的提交消息支持我的猜测,并暗示它与SharedNonNull(替换了Shared)之间的区别有关。 - trent
1个回答

10

PhantomData在此用于告诉drop检查器,丢弃Rc<T>可能会导致类型为T的值被丢弃

当我们宣布可能会丢弃类型T的值时,drop检查器确保T中的任何生命周期都比struct本身更长。正是这个检查防止了以下代码的编译。在这种情况下,Rc的通用参数是PeekOnDrop<&'a u8>,其生命周期为'a

use std::{fmt, rc::Rc};

struct PeekOnDrop<T: fmt::Debug>(T);

impl<T: fmt::Debug> Drop for PeekOnDrop<T> {
    fn drop(&mut self) {
        println!("{:?}", self.0);
    }   
}

struct SelfReferential<'a> {
   value: Box<u8>,
   rc: Option<Rc<PeekOnDrop<&'a u8>>>,
}

fn main() {
    let mut sr = SelfReferential {
        rc: None,
        value: Box::new(1),
    };

    sr.rc = Some(Rc::new(PeekOnDrop(&*sr.value)));

    // `sr` would be dropped here, which could drop `value` before `rc`.
    // The destructor of `PeekOnDrop` would then try to inspect the (dangling)
    // reference, resulting in UB!
}

如果想要了解底层逻辑的完整说明,请参见nomicon,但请注意,没有PeekOnDrop,前面的例子编译得很好。这是因为Rc<T>在其Drop impl中声明其泛型参数T#[may_dangle]。这样做,它承诺它的Drop实现除了(可能)丢弃它之外,不会对它拥有的T值进行任何操作。只有当丢弃检查器递归地检查PeekOnDropDrop实现并发现它可能访问T时,才会出现错误。

为了完整起见,这里有一个未定义的程序示例,它通过断言PeekOnDropDrop实现不使用#[may_dangle]访问T来欺骗。如果Rc未使用PhantomData声明可能丢弃T的值,则原始示例将展示相同的未定义行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接