【问题标题】:How do I return an Iterator over a collection encapsulated by a RefCell/RwLock Ref/Guard using unsafe code?如何使用不安全代码通过 RefCell/RwLock Ref/Guard 封装的集合返回迭代器?
【发布时间】:2019-01-22 15:11:25
【问题描述】:

已就该主题提出了多个问题:

答案或多或少:不可能(没有不安全)。

我自己尝试了不安全的变体,想问问这种方式是否安全。

我的想法是我将守卫包装在一个实现Iterator 的结构中。除了守卫之外,还存储了一个迭代器,该迭代器将从存储的守卫中创建:

struct MapIter<'a> {
    guard: RwLockReadGuard<'a, HashMap<i32, i32>>,
    iter:  Iter<'a, i32, i32>,
}

它是用这些行创建的:

impl<'a> MapIter<'a> {
    fn new(map: &'a RwLock<HashMap<i32, i32>>) -> Box<Self> {
        // create a `box Self`
        // the iterator remains uninitialized.
        let mut boxed = Box::new(Self {
            guard: map.read().expect("ToDo"),
            iter:  unsafe { mem::uninitialized() },
        });

        // create the iterator from `box Self`.
        boxed.iter = unsafe { 
            (*(&boxed.guard as *const RwLockReadGuard<'a, HashMap<i32, i32>>)).iter() 
        };
        boxed
    }
}

现在可以实现Iterator:

impl<'a> Iterator for MapIter<'a> {
    type Item = (&'a i32, &'a i32);

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }
}

这段代码安全吗?

playground 上查看此代码。

另外我得到一个琐碎的演员表警告

warning: trivial cast: warning: trivial cast: `&std::sync::RwLockReadGuard<'_, std::collections::HashMap<i32, i32>>` as `*const std::sync::RwLockReadGuard<'a, std::collections::HashMap<i32, i32>>`. Cast can be replaced by coercion, this might require type ascription or a temporary variable
   |
   | unsafe { (*(&boxed.guard as *const RwLockReadGuard<'a, HashMap<i32, i32>>)).iter() };
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |

如何解决这个问题?

【问题讨论】:

    标签: rust unsafe


    【解决方案1】:

    不,这不安全。我可以使用Container 在安全代码中创建一个悬空引用:

    let container = Container::new();       // create a container
    let r = {
        let mut it = container.iter();
        it.next()                           // obtain a reference to part of it
    };
    container.map.write().unwrap().clear(); // empty the container
    println!("{:?}", r);                    // oh dear.
    

    the playground 中编译,这不好,因为r 包含对在HashMap 被清除时无效的数据的引用。

    Vladimir Matveev's answer to a similar question 更详细地解释了为什么这是不合理的,并包含以下简明摘要:

    您不能这样做,因为它可以让您绕过运行时检查唯一性违规。

    【讨论】:

    • 假设你只返回值,即克隆Item,这样安全吗?
    • @Tim 如果next 返回Option&lt;(i32, i32)&gt;?我认为那是安全的,但我还没有完全相信自己的想法。
    • 是的,这实际上是我的用例,但我放弃了它以创建minimal reproducible example。实际上,我有一个 (&amp;u64, &amp;DirEntry) 将转换为 (u64, PathBuf)
    • @Tim 这是我目前发现的:通过类比Ref::map,这似乎应该是安全的;然而,与并发有一个微妙的交互,使得map 相当于RwLockReadGuard unsound, or at least potentially unsound with future changes to the stdlib on Windows。我不太了解这个问题,无法确定您提议的 MapIter 是否也有问题。
    • 但是,您似乎不太可能意外触发此健全性错误。因此,如果它不太可能被触发并且您甚至不确定它是否存在,您可能会发现风险是可以接受的。我相信API可以安全地实现,所以即使实现错误,将来也可以修复。至少保证iterguard之前被删除,以避免从那个角度出现问题。
    猜你喜欢
    • 2016-02-06
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 2021-05-09
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    相关资源
    最近更新 更多