“由于需求冲突,无法推断出 autoref 的适当生命周期”,但由于特征定义约束,无法更改任何内容

`cannot infer an appropriate lifetime for autoref due to conflicting requirements` but can't change anything due to trait definition constraints

我是按照 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`.

我看过

我懂一点,但不多。我在这里面临的问题是,如果我尝试更改任何内容,则会弹出一条错误消息,指出无法匹配特征定义。

我的想法是,基本上我需要以某种方式声明生命周期 '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)
    }
}

☝️这有效。

Rust 试图说你有一个悬空引用。

self.0.as_mut() // value borrowed
self.0 = <> // underlying value changed here. 

问题是下面的定义:

pub struct IterMut<'a,  T>(&'a mut Link<T>)

这无法封装您将有一个 "empty" 节点,意思是已到达节点的末尾。

使用书中提到的结构,如:

pub struct IterMut<'a,  T>(Option<&'a mut Node<T>>);

这确保您可以在 运行 列表末尾时将 None 留在原处,并使用 take 在幕后修改 IterMut 内容。

关键是你需要能够以某种方式从 &'b mut IterMut<'a, T>.

中提取 Option<&'a mut 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);
}

(playground link)

关键是,如果我们能够在较短的(通用)生命周期内借用一些东西 'b 并且 return 可以在较长的生命周期内进行修改 'a,我们' d 能够使用多个短生命周期(短于 'a 且不重叠)来获得具有相同生命周期的多个可变引用 'a.

这也解释了为什么不可变版本有效。使用不可变引用,从 &'b &'a T&'a T 是微不足道的:只需尊重并复制不可变引用。相比之下,可变引用不实现 Copy.


所以如果我们不能从 &'b mut &'a mut Link<T> 中生成 &'a mut Link<T>,我们当然也不能从中得到 Option<&'a mut T(除了 None ). (请注意,我们 可以 生成 &'b mut Link<T>,因此生成 Option<'b mut T>。这就是您的代码现在所做的。)

那么有什么用呢?请记住,我们的目标是能够从 &'b mut IterMut<'a, T>.

生成 Option<&'a mut 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
    })
}

(playground link)

最简单的设置方法是稍微调换 IterMut<'a, T> 以使其正常工作。与其在选项外设置可变引用,不如在选项内设置!现在你总能用 None!

生成 IterMut<'a, T>
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(这在前面链接列表太多中提到过)。

fn next<'b>(&'b mut self) -> Option<&'a mut T> {
    self.0.take().map(|node| {
        self.0 = node.next.as_mut();
        &mut node.elem
    })
}

(playground link)


这实际上最终与链接列表过多中的实现略有不同。该实现删除了 &mut Box<Node<T>> 的双重间接寻址,并将其替换为简单的 &mut Node<T>。但是,我不确定您获得了多少收益,因为该实现在 List::iter_mutIterator::next.

中仍然有双重取消引用