Rust 带回调的类'zip'函数中的借用检查器问题

Rust 带回调的类'zip'函数中的借用检查器问题,rust,borrow-checker,Rust,Borrow Checker,我试图实现一个函数,该函数同时遍历两个迭代器,为每对迭代器调用一个函数。这个回调可以通过返回(bool,bool)元组来控制在每个步骤中哪些迭代器是高级的。由于迭代器在我的用例中引用了缓冲区,因此它们不能实现stdlib中的迭代器特性,而是通过next\u ref函数使用,该函数与Iterator::next相同,但需要一个额外的生存期参数 // An iterator-like type, that returns references to itself // in next_ref str

我试图实现一个函数,该函数同时遍历两个迭代器,为每对迭代器调用一个函数。这个回调可以通过返回
(bool,bool)
元组来控制在每个步骤中哪些迭代器是高级的。由于迭代器在我的用例中引用了缓冲区,因此它们不能实现stdlib中的
迭代器
特性,而是通过
next\u 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)
    });
}
我理解它为什么抱怨,但找不到解决办法。我非常感谢在这个问题上的任何帮助


链接到。

中的此代码片段此代码的问题在于,
RefIter
以两种方式使用,这两种方式基本上是相互矛盾的:

  • next\u ref
    的调用者收到对存储值的引用,该值与
    RefIter
  • RefIter
    的值需要是可变的,以便在每次调用时可以递增
这完美地描述了可变别名(您试图在保留对“value”的引用时修改它)——Rust明确设计用于防止这种情况

为了使每个压缩的
都能工作,您需要修改
RefIter
,以避免传递对您希望变异的数据的引用。 我使用
RefCell
Rc
的组合实现了以下一种可能性:

use std::cell::RefCell;
use std::rc::Rc;

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


impl RefIter {
    fn next_ref(&self) -> Option<Rc<u64>> {
        let new_val = Rc::new(**self.value.borrow() + 1);
        *self.value.borrow_mut() = new_val;
        Some(Rc::clone(&*self.value.borrow()))
    }
}


// Iterate over two RefIter simultaniously 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>(iter1: RefIter, iter2: RefIter, callback: F)
    where F: Fn(&Option<Rc<u64>>, &Option<Rc<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 iter1 = RefIter { value: RefCell::new(Rc::new(3)) };
    let iter2 = RefIter { value: RefCell::new(Rc::new(4)) };
    each_zipped(iter1, iter2, |val1, val2| {
        // We can't use unwrap() directly, since we're only passed a reference to an Option
        let val1 = **val1.iter().next().unwrap();
        let val2 = **val2.iter().next().unwrap();
        println!("{}, {}", val1, val2);
        (val1 < 10, val2 < 10)
    });
}
使用std::cell::RefCell;
使用std::rc::rc;
//一种类似迭代器的类型,返回对自身的引用
//在下一个参考中
结构改装机{
值:RefCell
}
impl改装机{
fn next_ref(&self)->选项{
让new_val=Rc::new(**self.value.borrow()+1);
*self.value.borrow_mut()=new_val;
一些(Rc::clone(&*self.value.borrow())
}
}
//同时迭代两个重新安装程序并调用回调
//每一双。回调函数返回一个布尔元组
//指示哪些迭代器应该是高级的。
fn每个拉链(iter1:改装器,iter2:改装器,回拨:F)
其中F:Fn(&Option,&Option)->(bool,bool)
{
让mut current1=iter1.next_ref();
让mut current2=iter2.next_ref();
环路{
让前进标志=回调(¤t1和¤t2);
预赛{
(对,对)=>{
current1=iter1.next_ref();
current2=iter2.next_ref();
},
(对、错)=>{
current1=iter1.next_ref();
},
(假、真)=>{
current2=iter1.next_ref();
},
(假,假)=>{
返回
}
}
}
}
fn main(){
设iter1=RefIter{value:RefCell::new(Rc::new(3))};
设iter2=RefIter{value:RefCell::new(Rc::new(4))};
每个拉链(iter1、iter2、val1、val2){
//我们不能直接使用unwrap(),因为我们只传递了对选项的引用
设val1=**val1.iter().next().unwrap();
设val2=**val2.iter().next().unwrap();
println!(“{},{}”,val1,val2);
(val1<10,val2<10)
});
}
此版本的
RefIter
Rc
s分发给消费者,而不是参考资料。这避免了可变别名的问题-通过放置 将新的
Rc
插入外部
RefCell
。这样做的一个副作用是,消费者能够保留对缓冲区的“旧”引用(通过返回的
Rc
),即使在
RefIter
升级之后也是如此

由于迭代器在我的用例中引用了缓冲区,因此它们不能实现stdlib中的
迭代器
特性,而是通过
next\u 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)
    });
}
您正在描述一个流迭代器。这有一个板条箱,恰如其分地叫做。文档描述了您的问题(重点是我的):

而标准的迭代器trait的功能是基于
next
方法,
StreamingIterator
的功能基于 一对方法:
advance
get
。这从本质上分裂了
next
一半的逻辑(实际上,
StreamingIterator
next
方法 只调用
advance
,然后调用
get

这是必需的,因为Rust对借词的词法处理(更多 特别是缺少单笔进入、多笔退出借款)如果
StreamingIterator
的定义类似于
Iterator
,只需要一个
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)
    });
}
extern板条箱流式迭代器;
使用streaming_iterator::StreamingIterator;
fn每个压缩(mut iter1:A、mut iter2:B、回调:F)
哪里
A:StreamingIterator,
B:StreamingIterator,

F:例如,我认为你认为改装机本身的设计有缺陷是不对的。我们可以轻松地一次遍历一个迭代器:。当使用
Rc
时,我也可以使用普通的迭代器,但在我的用例中,我希望返回对同一缓冲区不同部分的引用(在读取新数据时也会发生变化)。问题似乎更多地与非词汇生存期有关。在每个压缩的
的开头,我们对
current1
进行可变引用。然后,我们使用该值调用回调。在这一点上,我们不再需要参考。我们可以随时打电话给你