我正在按照too many linked lists来实现链表。尝试实现iter_mut()
时,我自己做了,并编写了以下代码:
type Link<T> = Option<Box<Node<T>>>;
pub struct List<T> {
head: Link<T>,
}
struct Node<T> {
elem: T,
next: Link<T>,
}
impl<T> List<T> {
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::<T>(&mut self.head)
}
}
pub struct IterMut<'a, T>(&'a mut Link<T>);
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next<'b>(&'b mut self) -> Option<&'a mut T> {
self.0.as_mut().map(|node| {
self.0 = &mut (**node).next;
&mut (**node).elem
})
}
}
我要避免强制和省略,因为露骨让我了解更多。
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/third.rs:24:16
|
24 | self.0.as_mut().map(|node| {
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'b` as defined on the method body at 23:13...
--> src/third.rs:23:13
|
23 | fn next<'b>(&'b mut self) -> Option<&'a mut T> {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/third.rs:24:9
|
24 | self.0.as_mut().map(|node| {
| ^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 20:6...
--> src/third.rs:20:6
|
20 | impl<'a, T> Iterator for IterMut<'a, T> {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/third.rs:25:22
|
25 | self.0 = &mut (**node).next;
| ^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0495`.
我看过Cannot infer an appropriate lifetime for autoref due to conflicting requirements。
我了解一点,但了解不多。我在这里面临的问题是,如果我尝试更改任何内容,则会弹出一条错误消息,提示无法与特征定义匹配。
[我的想法是,基本上我需要说明寿命'b
比'a
长,即<'b : 'a>
,但我不知道该怎么做。另外,我具有类似的功能来实现iter()
,效果很好。这使我感到困惑,为什么iter_mut()
会产生此类错误。
type Link<T> = Option<Box<Node<T>>>;
pub struct Iter<'a, T>(&'a Link<T>);
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.0.as_ref().map(|node| {
self.0 = &((**node).next);
&((**node).elem)
})
}
}
impl<T> List<T> {
pub fn iter(&self) -> Iter<T> {
Iter::<T>(&self.head)
}
}
☝️这有效。
关键是您需要能够以某种方式从Option<&'a mut T>
中提取&'b mut IterMut<'a, T>
。
要了解为什么IterMut<'a, T> := &'a mut Link<T>
无法正常工作,您需要了解使用可变引用完全可以做什么。当然,答案几乎是一切。您可以从中复制数据,更改其值以及许多其他事情。您无法做的一件事就是使它无效。如果要移出可变引用下的数据,则必须将其替换为相同类型的内容(包括生存期)。
在next
的内部,self
本质上是&'b mut &'a mut Link<T>
。除非我们对T
有所了解(并且在这种情况下我们不知道),否则根本无法从中产生&'a mut Link<T>
类型的东西。例如,如果总体上可能,我们可以做到
fn bad<'a, 'b, T>(_x: &'b mut &'a mut T) -> &'a mut T {
todo!()
}
fn do_stuff<'a>(x: &'a mut i32, y: &'a mut i32) {
// lots of stuff that only works if x and y don't alias
*x = 13;
*y = 42;
}
fn main() {
let mut x: &mut i32 = &mut 0;
let y: &mut i32 = {
let z: &mut &mut i32 = &mut x;
bad(z)
};
// `x` and `y` are aliasing mutable references
// and we can use both at once!
do_stuff(x, y);
}
关键是,如果我们能够在短(通用)生命周期'b
中借用某些东西,并返回允许在更长生命周期'a
中进行修改的东西,那么我们将能够使用多个短生命周期(比'a
和非重叠)以获取具有相同生存期'a
的多个可变引用。
这也解释了为什么不可变版本有效。对于不可变引用,从&'b &'a T
到&'a T
很简单:只需引用并复制不可变引用即可。相反,可变引用没有实现Copy
。
因此,如果我们无法从&'a mut Link<T>
中产生&'b mut &'a mut Link<T>
,那么我们当然也无法从中获得Option<&'a mut T
(None
除外)。 (请注意,我们can会产生&'b mut Link<T>
,因此会产生Option<'b mut T>
。这就是您的代码现在所做的。)
那么起作用起作用吗?请记住,我们的目标是能够从Option<&'a mut T>
生成&'b mut IterMut<'a, T>
。
如果我们能够无条件产生IterMut<'a, T>
,我们可以(临时)替换为self
,因此可以直接访问与我们的列表相关的IterMut<'a, T>
。
// This actually type-checks!
fn next<'b>(&'b mut self) -> Option<&'a mut T> {
let mut temp: IterMut<'a, T> = todo!(); // obviously this won't work
std::mem::swap(&mut self.0, &mut temp.0);
temp.0.as_mut().map(|node| {
self.0 = &mut node.next;
&mut node.elem
})
}
最简单的方法来设置所有内容,只需稍微移调IterMut<'a, T>
。不要将可变引用放在选项之外,而要放在里面!现在,您始终可以使用IterMut<'a, T>
生成None
!
struct IterMut<'a, T>(Option<&mut Box<Node<T>>>);
翻译next
,我们得到
fn next<'b>(&'b mut self) -> Option<&'a mut T> {
let mut temp: IterMut<'a, T> = IterMut(None);
std::mem::swap(&mut self.0, &mut temp.0);
temp.0.map(|node| {
self.0 = node.next.as_mut();
&mut node.elem
})
}
更多地,我们可以使用Option::take
而不是std::mem::swap
(这在Too Many Linked Lists >>中已提到)。
fn next<'b>(&'b mut self) -> Option<&'a mut T> {
self.0.take().map(|node| {
self.0 = node.next.as_mut();
&mut node.elem
})
}
这实际上最终与链接列表太多
中的实现略有不同。该实现消除了&mut Box<Node<T>>
的双重间接性,并简单地将其替换为&mut Node<T>
。但是,我不确定您会获得多少收益,因为该实现在List::iter_mut
和Iterator::next
中仍然具有双重deref。Rust试图说您有一个悬而未决的参考文献。