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

  • 相关阅读:
    adb 连接小米1S真机调试
    【Docker】容器中找不到vi命令
    【BIEE】导出数据报错
    【SPSS】软件介绍
    【Oracle】查询字段的长度、类型、精度、注释等信息
    【BIRT】使用rptlibrary设置统一数据源
    【Oracle】迁移含有BLOG类型的字段的表
    【Linux】war包的解压与压缩
    【docker】挂载web应用
    【Docker】安装tomcat并部署应用
  • 原文地址:https://www.cnblogs.com/rgbit/p/15581846.html
Copyright © 2011-2022 走看看