The problem: I want an iterator over items under a guard (RwLockReadGuard
in this case). The items are not references, they are cloned.
It seems a lot like those questions:
- How do I return an Iterator over a collection encapsulated by a RefCell/RwLock Ref/Guard using unsafe code?
- Returning iterator of a Vec in a RefCell
- How do I return an iterator that has a reference to something inside a RefCell?
- How can I return an iterator over a locked struct member in Rust?
But maybe there is an additional flavour in this question I can't get my head around.
So below there is a StructWithRwLock
that implements an Iterator
. And there is a struct ManyStructWithRwLock
which (surprisingly) contains a vec of structs StructWithRwLock
. It also implements an iterator. The iterator of ManyStructWithRwLock
runs each iterator of its children and always produces the next least element. For that a min-heap (BinaryHeap
) structure is used. The iterator is not allowed to use allocations inside so it has to use an externally allocated BinaryHeap
.
There is a test in the end. In the test two copies of the iterators are created. Both are dropped in between. But compiler for some reason thinks that mutable reference to binary heap may be used when the binary heap itself is dropped. How come? So I get this error
error[E0597]: `test_struct` does not live long enough
--> src/test_example.rs:132:24
|
132 | let mut iter = test_struct.iter(&mut buffer_heap);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
142 | }
| -
| |
| `test_struct` dropped here while still borrowed
| borrow might be used here, when `buffer_heap` is dropped and runs the destructor for type `BinaryHeap<StructWithRwLockIter<'_>>`
|
= note: values in a scope are dropped in the opposite order they are defined
Why is this happening? Do I need unsafe for this? How can it be corrected?
The minimal running example:
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::sync::{Arc, RwLock, RwLockReadGuard};
type BufferHeap<'b> = BinaryHeap<StructWithRwLockIter<'b>>;
#[derive(Debug)]
pub struct StructWithRwLock {
inner: Arc<RwLock<Vec<usize>>>
}
impl StructWithRwLock {
pub fn iter(&self) -> StructWithRwLockIter {
let inner = self.inner.read().unwrap();
StructWithRwLockIter {
inner,
current_index: 0,
}
}
}
pub struct StructWithRwLockIter<'a> {
inner: RwLockReadGuard<'a, Vec<usize>>,
current_index: usize,
}
impl<'a> StructWithRwLockIter<'a> {
fn peek(&self) -> Option<usize> {
let entries = &self.inner;
if self.current_index >= entries.len() {
return None;
}
let item = entries.get(self.current_index).unwrap();
Some(*item)
}
}
impl<'a> Eq for StructWithRwLockIter<'a> {}
impl<'a> PartialEq<Self> for StructWithRwLockIter<'a> {
fn eq(&self, other: &Self) -> bool {
self.peek().eq(&other.peek())
}
}
impl<'a> PartialOrd<Self> for StructWithRwLockIter<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.peek().partial_cmp(&other.peek())
}
}
impl<'a> Ord for StructWithRwLockIter<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.peek().cmp(&other.peek())
}
}
impl<'a> Iterator for StructWithRwLockIter<'a> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
let entries = &self.inner;
if self.current_index >= entries.len() {
return None;
}
let item = entries.get(self.current_index).unwrap();
self.current_index += 1;
Some((*item).clone())
}
}
pub struct ManyStructWithRwLock {
items: Vec<StructWithRwLock>
}
impl ManyStructWithRwLock {
pub fn iter<'b, 'a:'b>(&'b self, ext_buffer: &'a mut BufferHeap<'b>) -> impl Iterator<Item = usize> +'b {
ext_buffer.clear();
for item in &self.items {
ext_buffer.push(item.iter());
}
kmerge(ext_buffer)
}
}
/// similar to itertools::kmerge_by but using extartnal buffer
pub fn kmerge<'b,'a:'b>(ext_buffer: &'a mut BufferHeap<'b>) -> KMergeStrWLockBy<'a, 'b>
{
KMergeStrWLockBy {
heap: ext_buffer,
}
}
pub struct KMergeStrWLockBy<'a, 'b>
{
heap: &'a mut BufferHeap<'b>,
}
impl<'a, 'b> Iterator for KMergeStrWLockBy<'a, 'b>
{
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
let mut next = self.heap.pop()?;
let item = next.next()?;
self.heap.push(next);
Some(item)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BinaryHeap;
#[test]
fn test_aggr_iterator() {
let mut buffer_heap = BinaryHeap::with_capacity(2);
let test_struct = ManyStructWithRwLock {
items: vec![
StructWithRwLock {
inner: Arc::new(RwLock::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
},
StructWithRwLock {
inner: Arc::new(RwLock::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
}
]
};
let mut iter = test_struct.iter(&mut buffer_heap);
for i in 0..10 {
let _ = iter.next().unwrap();
}
drop(iter);
let mut iter = test_struct.iter(&mut buffer_heap);
for i in 0..10 {
let _ = iter.next().unwrap();
}
drop(iter);
}
}