问题:我想要一个在守卫下的项目迭代器(在这种情况下为
RwLockReadGuard
)。这些项目不是参考,它们是克隆的。
好像很像那些问题:
但是也许这个问题还有其他的味道我无法理解。
所以下面有一个
StructWithRwLock
实现了一个Iterator
。并且有一个结构 ManyStructWithRwLock
,它(令人惊讶地)包含一个结构 StructWithRwLock
的 vec。它还实现了一个迭代器。 ManyStructWithRwLock
的迭代器运行其子代的每个迭代器,并始终产生下一个最小元素。为此,使用了最小堆 (BinaryHeap
) 结构。迭代器不允许在内部使用分配,因此它必须使用外部分配BinaryHeap
.
最后有一个测试。在测试中创建了迭代器的两个副本。两者都落在两者之间。但是出于某种原因,编译器认为当二进制堆本身被删除时,可能会使用对二进制堆的可变引用。怎么会?所以我得到这个错误
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
为什么会这样?我需要不安全吗?如何纠正?
最小运行示例:
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);
}
}
这看起来像是通过将项目转发到
test_struct
的可变引用中来在 buffer_heap
中借用 buffer_heap
,而抱怨 test_struct
被删除得太早就是一个指标。即:
impl StructWithRwLock {
pub fn iter(&self) -> StructWithRwLockIter {
let inner = self.inner.read().unwrap(); // <-- mutable borrow of the vec inside Inner, requires a borrow of self
StructWithRwLockIter { // <-- therefore this returns an object that borrows self
inner,
current_index: 0,
}
}
}
Iterator 结构借用父结构是很正常的,因为它必须迭代元素。然而,在这里:
// ManyStructWithRwLock
for item in &self.items { // <-- borrow of self to borrow items
ext_buffer.push(item.iter()); // <-- pushes a reference within item, which therefore borrows item, which therefore borrows self
}
不是将单个结构的克隆项推入借用的缓冲区,而是推入每个结构的 Iterator,迭代器如图所示借用
test_struct
,所以 buffer_heap
现在借用 test_struct
。迭代器在用完项目时不会自动删除,它们只是处于“完成”状态,并且在这里它们仍在堆中(而且我不确定借用检查器是否会理解你是否如果您迭代 11 而不仅仅是 10 或进行清除,则能够删除它们)。
你可以急切地从你的内部结构中克隆并将它们直接放在缓冲区中(但这是一个分配),或者将你的结构向量包装在
Rc
或 Cell
中并克隆它们——这仍然是一个用于复制指针的分配,但至少它没有分配向量的全部内容。或者您可以要求将缓冲区移动到ManyStructWithRwLock::iter
中,这样它也由数据的迭代器拥有,因此在迭代器是时被删除。
作为 Rust 的新手,他反复遇到这个问题,试图对意外借用到其他对象的东西进行可变引用......试图在结构中保存引用非常难以维护。