【问题标题】:Why does std::rc::Rc need PhantomData?为什么 std::rc::Rc 需要 PhantomData?
【发布时间】:2019-09-20 11:50:29
【问题描述】:

我知道PhantomData 旨在消耗数据类型定义中的生命周期或类型参数,否则这些参数将不会被使用。我最近查看了Rust std libRc 的定义,并注意到它似乎使用了PhantomData,但看起来T 在兄弟字段ptr 中被用作NonNull<RcBox<T>>。文档说 NonNull"*mut T but non-zero and covariant." 并继续使用以下语句进一步扩展该定义:

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

因此,它是否需要方差或更多是因为NonNull 实际上是一个原始指针,并且需要PhantomData 才能像this answer seems to imply 一样消耗省略的生命周期?

【问题讨论】:

  • 什么省略了生命周期?没有可以忽略的生命周期。
  • 啊,对了,因为没有明确的生命周期,T 的边界,任何类型的引用,以及原始指针,我们都没有提到生命周期,但这是否意味着PhantomData 那么是为子类型问题提供的吗?我引用的后面的句子确实继续说“通常这不是必需的;协方差对于大多数安全抽象都是正确的,例如 Box、Rc、Arc、Vec 和 LinkedList。” iiuc 这意味着提供幻像类型会使外壳类型在T 上保持不变,但随后Rc 使用它,这句话暗示它在T 上是协变的?
  • Rc 并不总是有 PhantomData 字段。这是引入它的提交:github.com/rust-lang/rust/commit/…
  • 提交消息引用了this conversation,但这似乎很老了,并不能完全解释为什么这样做。
  • 我猜它与删除逻辑有关,与生命周期或子类型无关。我认为,@PeterHall 链接到的提交消息支持了我的猜测,并表明它与 SharedNonNull 之间的区别有关(替换了 Shared)。

标签: rust


【解决方案1】:

PhantomData 在这里用于告诉 drop checker 正在删除 Rc<T> may cause a value of type T to be dropped

当我们宣布可以删除 T 类型的值时,删除检查器会确保 T 中的任何生命周期都比 struct 本身的生命周期长。正是这个检查阻止了following code 的编译。在这种情况下,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!
}

这里底层逻辑的完整解释见the nomicon,但注意没有PeekOnDrop,前面的例子compiles just fine。这是因为Rc&lt;T&gt; 在其Drop impl 中声明其通用参数T#[may_dangle]。在这样做的过程中,它承诺它的Drop impl 不会对它拥有的T 的值做任何事情,除非(可能)放弃它。只有当 drop checker 递归检查 PeekOnDropDrop impl 并发现它可以访问 T 时才会发生错误。

为了完整起见,here's an example of an undefined program 的谎言在于断言 PeekOnDropDrop impl 不会使用 #[may_dangle] 访问 T。如果Rc 没有使用PhantomData 来声明它可能会删除T 的值,那么原始示例中也会出现相同的未定义行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多