gpt4 book ai didi

iterator - 如何实现赋予结构生命周期的迭代器?

转载 作者:行者123 更新时间:2023-11-29 08:00:33 25 4
gpt4 key购买 nike

我想创建一个控制修改的可变迭代器,为此,我创建了一个名为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

最佳答案

除非您可以不实现标准的Iterator特征,否则您将无法使它与简单的可变引用一起使用。这是因为在Rust中,对一个特定值同时具有多个可用的可变别名是不合法的(因为这可能导致内存不安全)。让我们看看为什么您的代码违反了此限制。

首先,我可以实例化FitnessIterMut对象。从该对象,我可以调用next以获得FitnessModifier。此时,FitnessIterMutFitnessModifier都包含对RouletteWheel对象的可变引用,并且FitnessIterMutFitnessModifier仍然可用–这是不合法的!我可以再次在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>>,其中 'bself参数的生存期链接。当初始化结果 wheelFitnessModifier字段时,编译器将自动从 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'b长,这就是为什么会出现编译器错误的原因。

如果Rust不允许重新借用,那么您不必存储 &'b mut RouletteWheel<T>FitnessModifier中,而必须存储 &'b &'a mut RouletteWheel<T>,其中 'aRouletteWheel<T>的生存期, 'b&'a mut RouletteWheel<T>的生存期在 FitnessIterMut<'a, T>中。但是,Rust使我们可以将该引用“折叠”为引用,并且我们可以只存储 &'b mut RouletteWheel<T>(生存期为 'b,而不是 'a,因为 'b的生存期较短)。

此更改的最终结果是,在调用 next之后,您将无法使用 FitnessIterMut,直到结果 Option<FitnessModifier<'b, T>>超出范围。这是因为 FitnessModifier是从 self借用的,并且由于该方法是通过可变引用传递 self的,因此编译器假定 FitnessModifier保留了对 FitnessIterMut或其字段之一的可变引用(在此是正确的,但并非总是如此一般的)。因此,尽管范围内有一个 FitnessModifier,但 RouletteWheel仅有一个可用的可变别名,它是 FitnessModifier对象中的一个别名。当 FitnessModifier<'b, T>超出范围时, FitnessIterMut对象将再次变得可用。

如果您绝对需要遵循 Iterator特质,那么我建议您将可变引用替换为 Rc<RefCell<T>> Rc 没有实现 Copy,但是实现了 Clone(仅克隆指针,而不克隆基础数据),因此您需要显式调用 .clone()来克隆 Rc RefCell 在运行时进行动态借阅检查,这有一点运行时开销,但在如何传递可变对象方面给了您更多自由。

关于iterator - 如何实现赋予结构生命周期的迭代器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37366203/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com