Rust Inspect Iterator:不能借用`*`作为不可变的,因为它也被借用为可变的

Rust Inspect Iterator: cannot borrow `*` as immutable because it is also borrowed as mutable

为什么我不能在 inspect 期间 push 到此矢量并在 skip_while 期间对其执行 contains

我已经为自己的结构 Chain 实现了自己的迭代器,如下所示:

struct Chain {
    n: u32,
}

impl Chain {
    fn new(start: u32) -> Chain {
        Chain { n: start }
    }
}

impl Iterator for Chain {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        self.n = digit_factorial_sum(self.n);
        Some(self.n)
    }
}

现在我想做什么 take 当迭代器生成唯一值时。所以我正在 inspect-ing 链并推送到一个向量,然后在 take_while 范围内检查它:

let mut v = Vec::with_capacity(terms);
Chain::new(i)
    .inspect(|&x| {
        v.push(x)
    })
    .skip_while(|&x| {
        return v.contains(&x);
    })

但是,Rust 编译会报错:

error: cannot borrow `v` as immutable because it is also borrowed as mutable [E0502]
...
borrow occurs due to use of `v` in closure
    return v.contains(&x);
           ^
previous borrow of `v` occurs here due to use in closure; the mutable borrow prevents subsequent moves, borrows, or modification of `v` until the borrow ends
    .inspect(|&x| {
        v.push(x)
    })

显然我不明白"borrowing"的概念。我做错了什么?

这里的问题是您试图创建对同一变量的可变引用和不可变引用,这违反了 Rust 借用规则。而 rustc 实际上确实很清楚地告诉你这一点。

let mut v = Vec::with_capacity(terms);
Chain::new(i)
    .inspect(|&x| {
        v.push(x)
    })
    .skip_while(|&x| {
        return v.contains(&x);
    })

这里您尝试在两个闭包中使用 v,第一个在 inspect() 参数中,第二个在 skip_while() 参数中。非 move 闭包通过引用捕获它们的环境,因此第一个闭包的环境包含 &mut v,第二个闭包的环境包含 &v。闭包是在同一个表达式中创建的,所以即使它是 gua运行teed inspect() 运行 并在 skip_while() 之前删除借用(我不是实际情况,因为这些是迭代器适配器,在迭代器被使用之前它们根本不会 运行),由于词法借用规则,这是被禁止的。

不幸的是,这是借用检查器过于严格的例子之一。你可以做的是使用 RefCell,它允许通过共享引用进行突变,但会引入一些 运行-时间成本:

use std::cell::RefCell;

let mut v = RefCell::new(Vec::with_capacity(terms));
Chain::new(i)
    .inspect(|x| v.borrow_mut().push(*x))
    .skip_while(|x| v.borrow().contains(x))

认为 可以避免 运行 RefCell 的时间损失并改用 UnsafeCell ,因为当迭代器是消耗后,这些闭包只会 运行 一个接一个,而不是同时出现,所以永远不应该同时存在一个可变和一个不可变引用。它可能看起来像这样:

use std::cell::UnsafeCell;

let mut v = UnsafeCell::new(Vec::with_capacity(terms));
Chain::new(i)
    .inspect(|x| unsafe { (&mut *v.get()).push(*x) })
    .skip_while(|x| unsafe { (&*v.get()).contains(x) })

但我可能错了,无论如何,RefCell 的开销并没有那么高,除非这段代码 运行ning 在 really 紧循环,所以你应该只使用 UnsafeCell 作为最后的手段,只有在没有其他方法时才使用它,并且在使用它时要格外小心。