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
作为最后的手段,只有在没有其他方法时才使用它,并且在使用它时要格外小心。
为什么我不能在 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
作为最后的手段,只有在没有其他方法时才使用它,并且在使用它时要格外小心。