Reputation: 8486
Multiple questions were already asked regarding this topic:
The answers are more or less: It's not possible (without unsafe).
I tried the unsafe variant myself and want to ask if this way is safe.
The idea is that I wrap the guard in a struct that implements Iterator
. Besides the guard, an iterator is stored which will be created from the stored guard:
struct MapIter<'a> {
guard: RwLockReadGuard<'a, HashMap<i32, i32>>,
iter: Iter<'a, i32, i32>,
}
It's created with these lines:
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
}
}
Now it can implement Iterator
:
impl<'a> Iterator for MapIter<'a> {
type Item = (&'a i32, &'a i32);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
Is this code safe?
See this code in action at the playground.
Additionally I get a trivial cast warning
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() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
How to get around this?
Upvotes: 1
Views: 796
Reputation: 28055
No, it's not safe. I can use Container
to create a dangling reference in safe code:
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.
In the playground this compiles, which isn't good, because r
contains references to data that are invalidated when the HashMap
is cleared.
Vladimir Matveev's answer to a similar question explains in more detail why this is unsound, and contains the following concise summary:
You cannot do this because it would allow you to circumvent runtime checks for uniqueness violations.
Upvotes: 4