我想创建一个控制修改的可变迭代器,为此我创建了一个名为FitnessIterMut
的结构impl
Iterator
特征。
next()
方法提供了一个结构,可以在修改完成后对容器本身执行操作。 (这是做这种事情的好方法吗?)
pub struct FitnessModifier<'a, T: 'a> {
wheel: &'a mut RouletteWheel<T>,
value: &'a mut (f32, T)
}
impl<'a, T> FitnessModifier<'a, T> {
pub fn read(&'a self) -> &'a (f32, T) {
self.value
}
pub fn set_fitness(&'a self, new: f32) {
let &mut (ref mut fitness, _) = self.value;
self.wheel.proba_sum -= *fitness;
self.wheel.proba_sum += new;
*fitness = new;
}
}
pub struct FitnessIterMut<'a, T: 'a> {
wheel: &'a mut RouletteWheel<T>,
iterator: &'a mut IterMut<'a, (f32, T)>
}
impl<'a, T> Iterator for FitnessIterMut<'a, T> {
type Item = FitnessModifier<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(value) = self.iterator.next() {
Some(FitnessModifier { wheel: self.wheel, value: value })
}
else {
None
}
}
}
这给了我这个错误,我想我必须做'b
一生,但我有点迷失。
error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements [E0495]
Some(FitnessModifier { wheel: self.wheel, value: value })
^~~~~~~~~~
help: consider using an explicit lifetime parameter as shown: fn next(&'a mut self) -> Option<Self::Item>
fn next(&mut self) -> Option<Self::Item> {
if let Some(value) = self.iterator.next() {
Some(FitnessModifier { wheel: self.wheel, value: value })
}
else {
None
答案 0 :(得分:2)
除非你没有不实现标准Iterator
特征,否则你将无法使用简单的可变引用。这是因为在Rust中同时为特定值提供多个可用的可变别名是不合法的(因为它可能导致内存不安全)。让我们看看您的代码违反此限制的原因。
首先,我可以实例化一个FitnessIterMut
对象。通过此对象,我可以致电next
获取FitnessModifier
。此时,FitnessIterMut
和FitnessModifier
都包含对RouletteWheel
对象的可变引用,FitnessIterMut
和FitnessModifier
仍可使用 - 那不合法!我可以在next
再次致电FitnessIterMut
以获取另一个FitnessModifier
,现在我将RouletteWheel
有3个可变别名。
您的代码无法编译,因为您认为可以复制可变引用,但实际情况并非如此。不可变引用(&'a T
)实现Copy
,但可变引用(&'a mut T
)不实现,因此无法复制它们。
我们可以做些什么来解决这个问题? Rust允许我们通过重新借用暂时使可变引用无法使用(即,如果您尝试使用它,将会遇到编译器错误)。通常,对于不实现Copy
的类型,编译器将移动值而不是复制它,但对于可变引用,编译器将重新扩展而不是移动。重新生长可以被视为“扁平化”或“折叠”对参考文献的引用,同时保持最短的生命周期。
让我们看看它在实践中是如何运作的。这是next
的有效实现。请注意,这不符合标准Iterator
特征的合同,因此我将其作为一种固有的方法。
impl<'a, T> FitnessIterMut<'a, T> {
fn next<'b>(&'b mut self) -> Option<FitnessModifier<'b, T>> {
if let Some(value) = self.iterator.next() {
Some(FitnessModifier { wheel: self.wheel, value: value })
}
else {
None
}
}
}
我们现在返回一个Option<FitnessModifier<'a, T>>
,而不是返回Option<FitnessModifier<'b, T>>
,其中'b
与self
参数的生命周期相关联。在初始化结果wheel
的{{1}}字段时,编译器将自动从FitnessModifier
重新继承(我们可以通过编写self.wheel
而不是&mut *self.wheel
来明确说明这一点)
由于此表达式引用了self.wheel
,因此您认为此表达式的类型也是&'a mut RouletteWheel<T>
。但是,因为此表达式借用&'a mut RouletteWheel<T>
self
,所以此表达式的类型实际为&'b mut FitnessIterMut<'a, T>
。在您的代码中,您尝试将&'b mut RouletteWheel<T>
分配给期望&'b mut RouletteWheel<T>
的字段,但&'a mut RouletteWheel<T>
长于'a
,这就是您遇到编译错误的原因。< / p>
如果Rust不允许重新生成,那么您不必在'b
中存储&'b mut RouletteWheel<T>
,而是必须存储FitnessModifier
,其中&'b &'a mut RouletteWheel<T>
是生命周期'a
和RouletteWheel<T>
是'b
中&'a mut RouletteWheel<T>
的生命周期。但是,Rust允许我们将此引用“折叠”为引用,我们只能存储FitnessIterMut<'a, T>
(生命周期为&'b mut RouletteWheel<T>
,而不是'b
,因为'a
是寿命缩短。
此更改的最终效果是,在您致电'b
后,在结果next
超出范围之前,您根本无法使用FitnessIterMut
。这是因为Option<FitnessModifier<'b, T>>
是从FitnessModifier
借用的,并且由于该方法通过可变引用传递self
,编译器假定self
保持对{{1}的可变引用或者它的一个字段(这里是真的,一般情况下并非总是如此)。因此,虽然范围内有FitnessModifier
,但FitnessIterMut
只有一个可用的可变别名,这是FitnessModifier
对象中的一个。当RouletteWheel
超出范围时,FitnessModifier
对象将再次可用。
如果您完全需要符合FitnessModifier<'b, T>
特征,那么我建议您使用FitnessIterMut
替换您的可变引用。 Rc
未实现Iterator
,但它实现Rc<RefCell<T>>
(仅克隆指针,而不是基础数据),因此您需要显式调用Copy
来克隆Clone
。 RefCell
在运行时进行动态借用检查,这会带来一些运行时开销,但可以让您更自由地传递可变对象。