带有回调的`zip`-like函数中的借用检查器问题

ase*_*ldt 3 rust borrow-checker

我正在尝试实现一个同时逐步执行两个迭代器的函数,为每对调用一个函数.此回调可以通过返回(bool, bool)元组来控制每个步骤中哪个迭代器前进.由于迭代器在我的用例中引用了一个缓冲区,它们无法实现Iteratorstdlib中的特征,而是通过一个next_ref函数来使用,该函数与之相同Iterator::next,但需要额外的生命周期参数.

// An iterator-like type, that returns references to itself
// in next_ref
struct RefIter {
    value: u64
}

impl RefIter {
    fn next_ref<'a>(&'a mut self) -> Option<&'a u64> {
        self.value += 1;
        Some(&self.value)
    }
}

// Iterate over two RefIter simultaneously and call a callback
// for each pair. The callback returns a tuple of bools
// that indicate which iterators should be advanced.
fn each_zipped<F>(mut iter1: RefIter, mut iter2: RefIter, callback: F)
    where F: Fn(&Option<&u64>, &Option<&u64>) -> (bool, bool)
{
    let mut current1 = iter1.next_ref();
    let mut current2 = iter2.next_ref();
    loop {
        let advance_flags = callback(&current1, &current2);
        match advance_flags {
            (true, true) => {
                current1 = iter1.next_ref();
                current2 = iter2.next_ref();
            },
            (true, false) => {
                current1 = iter1.next_ref();
            },
            (false, true) => {
                current2 = iter1.next_ref();
            },
            (false, false) => {
                return
            }
        }
    }
}

fn main() {
    let mut iter1 = RefIter { value: 3 };
    let mut iter2 = RefIter { value: 4 };
    each_zipped(iter1, iter2, |val1, val2| {
        let val1 = *val1.unwrap();
        let val2 = *val2.unwrap();
        println!("{}, {}", val1, val2);
        (val1 < 10, val2 < 10)
    });
}
Run Code Online (Sandbox Code Playgroud)
error[E0499]: cannot borrow `iter1` as mutable more than once at a time
  --> src/main.rs:28:28
   |
22 |     let mut current1 = iter1.next_ref();
   |                        ----- first mutable borrow occurs here
...
28 |                 current1 = iter1.next_ref();
   |                            ^^^^^ second mutable borrow occurs here
...
42 | }
   | - first borrow ends here

error[E0499]: cannot borrow `iter2` as mutable more than once at a time
  --> src/main.rs:29:28
   |
23 |     let mut current2 = iter2.next_ref();
   |                        ----- first mutable borrow occurs here
...
29 |                 current2 = iter2.next_ref();
   |                            ^^^^^ second mutable borrow occurs here
...
42 | }
   | - first borrow ends here

error[E0499]: cannot borrow `iter1` as mutable more than once at a time
  --> src/main.rs:32:28
   |
22 |     let mut current1 = iter1.next_ref();
   |                        ----- first mutable borrow occurs here
...
32 |                 current1 = iter1.next_ref();
   |                            ^^^^^ second mutable borrow occurs here
...
42 | }
   | - first borrow ends here

error[E0499]: cannot borrow `iter1` as mutable more than once at a time
  --> src/main.rs:35:28
   |
22 |     let mut current1 = iter1.next_ref();
   |                        ----- first mutable borrow occurs here
...
35 |                 current2 = iter1.next_ref();
   |                            ^^^^^ second mutable borrow occurs here
...
42 | }
   | - first borrow ends here
Run Code Online (Sandbox Code Playgroud)

我明白为什么抱怨,但无法找到解决办法.我很感激有关这个问题的任何帮助.

链接到操场上的这个片段.

She*_*ter 5

由于迭代器在我的用例中引用了一个缓冲区,它们无法实现Iteratorstdlib中的特征,而是通过一个next_ref函数来使用,该函数与之相同Iterator::next,但需要额外的生命周期参数.

您正在描述流式迭代器.有一个箱子,恰当地称为streaming_iterator.文档描述了您的问题(强调我的):

虽然标准Iterator特征的功能基于该next方法,但其StreamingIterator功能基于一对方法:advanceget.这实质上分裂的逻辑next一半(实际上StreamingIteratornext方法不执行任何操作,但调用advance之后get).

这是必需的,因为Rust对借用的词汇处理(更具体地说是缺少单一条目,多次退出借阅).如果 StreamingIteratorIterator只需要一个必需的 next方法一样filter定义,就不可能定义类似的操作.

箱子目前没有zip功能,当然也不是你所描述的变种.但是,它很容易实现:

extern crate streaming_iterator;

use streaming_iterator::StreamingIterator;

fn each_zipped<A, B, F>(mut iter1: A, mut iter2: B, callback: F)
where
    A: StreamingIterator,
    B: StreamingIterator,
    F: for<'a> Fn(Option<&'a A::Item>, Option<&'a B::Item>) -> (bool, bool),
{
    iter1.advance();
    iter2.advance();

    loop {
        let advance_flags = callback(iter1.get(), iter2.get());
        match advance_flags {
            (true, true) => {
                iter1.advance();
                iter2.advance();
            }
            (true, false) => {
                iter1.advance();
            }
            (false, true) => {
                iter1.advance();
            }
            (false, false) => return,
        }
    }
}

struct RefIter {
    value: u64
}

impl StreamingIterator for RefIter {
    type Item = u64;

    fn advance(&mut self) {
        self.value += 1;
    }

    fn get(&self) -> Option<&Self::Item> {
        Some(&self.value)
    }
}

fn main() {
    let iter1 = RefIter { value: 3 };
    let iter2 = RefIter { value: 4 };
    each_zipped(iter1, iter2, |val1, val2| {
        let val1 = *val1.unwrap();
        let val2 = *val2.unwrap();
        println!("{}, {}", val1, val2);
        (val1 < 10, val2 < 10)
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 对于那种特殊的疯狂没有办法,这是我从实际代码中删除东西时所做的复制和粘贴错误.这个特定版本的`zip`的原因是fastq记录(关于DNA片段的信息)经常成对出现,分成两个不同的文件.但有时这些文件包含几个不成对的记录,这会导致普通的zip不同步.顺便说一句,拆分迭代器是个好主意.我想我错过了关键字`streaming iterator`,这让google搜索变得非常困难.谢谢. (2认同)