First, re-using a lifetime parameter at two "levels" like MutexGuard<'a, hash_map::Iter<'a, K, V>>
does is an antipattern and almost never what you want (even when both lifetimes are in covariant position, as here). Your default should be to use two different lifetimes:
pub struct LockedIter<'a, 'b, K, V> {
inner: MutexGuard<'a, hash_map::Iter<'b, K, V>>,
}
If you try to write code against the one-lifetime version you're liable to run into puzzling compiler errors.
Second, you can get &'short T
or &'short mut T
from &'short MutexGuard<'long, T>
via the Deref
and DerefMut
implementations, but you can't get &'long T
or &'long mut T
from MutexGuard<'long, T>
, because it would allow this (playground):
fn do_something_unsound(_guard: MutexGuard<'long, i32>) -> &'long i32 {
...
}
fn break_stuff() {
let mutex = Mutex::new(123);
{
let borrowed_mutex /* : &'long Mutex<i32> */ = &mutex;
let first_borrow /* : &'long i32 */ = {
let guard /* : MutexGuard<'long, i32> */ = Mutex::lock(borrowed_mutex).unwrap();
do_something_unsound(guard)
// guard is dropped here, but first_borrow will outlive it
};
// create and use a mutable reference to the contained value
// this *should* invalidate any existing references to the value...
*mutex.lock().unwrap() = 456;
// ...but first_borrow is still valid here!
dbg!(first_borrow);
}
}
So I don't think what you're envisioning is possible: the yielded items would have to borrow from the LockedIter
, which Iterator
doesn't support (but a future GAT-enabled LendingIterator
trait would). Instead, how about an "internal iteration" approach?
impl<'a, 'b: 'a, K, V> LockedIter<'a, 'b, K, V> {
pub fn for_each_pair<F>(mut self, mut f: F)
where
F: FnMut(&K, &V),
{
for (k, v) in &mut *self.inner {
f(k, v);
}
}
}
You can also have other methods in the for_each
mold that support returning early, passing data between iterations (like fold
), etc.