zoukankan      html  css  js  c++  java
  • Rust PhantomData and dropck rgb

    在 Rust 标准库中,类似 Rc、LinkedList 的结构体都持有一个 PhantomData 类型的字段,当我们自己去实现一个类似的结构体的时候,会发现即使没有这个字段好像也没有问题,编译器能够正常编译通过(linked_list impl)。

    标准库中的结构体会持有一个类型为 PhantomData 的字段是因为它们用特殊的方式实现了 Drop 这个 trait,以 std::collections::LinkedList 为例:

    #[stable(feature = "rust1", since = "1.0.0")]
    #[cfg_attr(not(test), rustc_diagnostic_item = "LinkedList")]
    #[rustc_insignificant_dtor]
    pub struct LinkedList<T> {
        head: Option<NonNull<Node<T>>>,
        tail: Option<NonNull<Node<T>>>,
        len: usize,
        marker: PhantomData<Box<Node<T>>>,
    }
    
    struct Node<T> {
        next: Option<NonNull<Node<T>>>,
        prev: Option<NonNull<Node<T>>>,
        element: T,
    }
    
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<#[may_dangle] T> Drop for LinkedList<T> {
        fn drop(&mut self) {
            struct DropGuard<'a, T>(&'a mut LinkedList<T>);
    
            impl<'a, T> Drop for DropGuard<'a, T> {
                fn drop(&mut self) {
                    // Continue the same loop we do below. This only runs when a destructor has
                    // panicked. If another one panics this will abort.
                    while self.0.pop_front_node().is_some() {}
                }
            }
    
            while let Some(node) = self.pop_front_node() {
                let guard = DropGuard(self);
                drop(node);
                mem::forget(guard);
            }
        }
    }
    

    可以看到,LinkedList 实现 Drop 时带有 unsafe impl<#[may_dangle] T> 的标记,这个标记告诉编译器 LinkedList 可能持有指向已 drop 对象的指针,但不会使用。

    这可以让下面的代码通过编译:

    use std::collections::LinkedList;
    
    fn main() {
        let mut list = LinkedList::new();
        let s = String::from("Hello World!");
        list.push_back(&s);
    }
    

    但如果实现的是普通的 Drop 方法,就会报错(当然不实现 Drop 就没问题):

    struct Container<T>(T);
    
    impl<T> Drop for Container<T> {
        fn drop(&mut self) {
            // ...
        }
    }
    
    fn main() {
        let mut container = Container(None);
        let s = String::from("Hello World!");
        container.0 = Some(&s);  // borrowed value does not live long enough
    }
    

    这里是因为我们自己实现了 Drop 这个 trait 后,编译器就将这个结构的 drop 交给我们自己的实现了,这时 s 会比 Container 先 drop, 编译器不确定我们自己的 drop 方法里面会不会使用 &s 这个引用,就直接编译失败了。

    unsafe impl<#[may_dangle] T> 便告诉了编译器,我们会处理好这种情况。

    那么为什么还需要持有 PhantomData 类型的字段呢?这是因为 T 也有可能实现自己的 Drop,LinkedList 没法保证 T 的实现是安全的, 所以使用 PhantomData 字段告诉编译器,如果 T: Drop 就需要做相关的检查,确保安全。

    参考:PhantomData and dropck confusion - help - The Rust Programming Language Forum

  • 相关阅读:
    noi 2011 noi嘉年华 动态规划
    最小乘积生成树
    noi 2009 二叉查找树 动态规划
    noi 2010 超级钢琴 划分树
    noi 2011 阿狸的打字机 AC自动机
    noi 2009 变换序列 贪心
    poj 3659 Cell Phone Network 动态规划
    noi 2010 航空管制 贪心
    IDEA14下配置SVN
    在SpringMVC框架下建立Web项目时web.xml到底该写些什么呢?
  • 原文地址:https://www.cnblogs.com/rgbit/p/15581846.html
Copyright © 2011-2022 走看看