`由于需求冲突而无法为autoref推断合适的生存期,但由于特征定义约束而不能更改任何内容

问题描述 投票:1回答:2

我正在按照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()会产生此类错误。

Iter

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 lifetime
2个回答
2
投票

关键是您需要能够以某种方式从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);
}

(playground link)

关键是,如果我们能够在短(通用)生命周期'b中借用某些东西,并返回允许在更长生命周期'a中进行修改的东西,那么我们将能够使用多个短生命周期(比'a和非重叠)以获取具有相同生存期'a的多个可变引用。

这也解释了为什么不可变版本有效。对于不可变引用,从&'b &'a T&'a T很简单:只需引用并复制不可变引用即可。相反,可变引用没有实现Copy


因此,如果我们无法从&'a mut Link<T>中产生&'b mut &'a mut Link<T>,那么我们当然也无法从中获得Option<&'a mut TNone除外)。 (请注意,我们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
    })
}

(playground link)

最简单的方法来设置所有内容,只需稍微移调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
    })
}

(playground link)


这实际上最终与链接列表太多

中的实现略有不同。该实现消除了&mut Box<Node<T>>的双重间接性,并简单地将其替换为&mut Node<T>。但是,我不确定您会获得多少收益,因为该实现在List::iter_mutIterator::next中仍然具有双重deref。

1
投票

Rust试图说您有一个悬而未决的参考文献。

© www.soinside.com 2019 - 2024. All rights reserved.