我如何使用不安全代码 return 对由 RefCell/RwLock Ref/Guard 封装的集合进行迭代?
How do I return an Iterator over a collection encapsulated by a RefCell/RwLock Ref/Guard using unsafe code?
关于这个主题已经提出了多个问题:
答案或多或少是:不可能(没有unsafe)
我自己尝试了不安全的变体,想问一下这种方式是否安全。
我的想法是将守卫包装在一个实现 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() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
如何解决这个问题?
不,这不安全。我可以使用 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
时无效。
更详细地解释了为什么这是不合理的,并包含以下简明摘要:
You cannot do this because it would allow you to circumvent runtime checks for uniqueness violations.
关于这个主题已经提出了多个问题:
答案或多或少是:不可能(没有unsafe)
我自己尝试了不安全的变体,想问一下这种方式是否安全。
我的想法是将守卫包装在一个实现 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() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
如何解决这个问题?
不,这不安全。我可以使用 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
时无效。
You cannot do this because it would allow you to circumvent runtime checks for uniqueness violations.