允许引用生命周期超过闭包

4

我正在尝试实现一个单向链表来熟悉Rust编程语言。以下是我的数据结构:

struct Linked<T> {
    head: Option<Box<Node<T>>>
}

struct Node<T> {
    data: T,
    next: Option<Box<Node<T>>>
}

现在,我想给这个添加一个迭代器:
struct LinkedIter<'a, T: 'a> {
    node: Option<&'a Node<T>>,
}

我已经为Linked<T>编写了一个.iter()方法,它可以编译并正常工作。

impl<T> Linked<T> {
    fn iter(&self) -> LinkedIter<T> {
        LinkedIter { node: match self.head {
                Some(ref node) => Some(&**node),
                None           => None
            }
        }
    }
}

现在,这个match块正在将一个Option<Box<Linked<T>>>转换为一个Option<&Linked<T>>。这正是Option::map()方法的用途。因此,我重新实现了这个方法,使用head.as_ref()代替head,以避免在闭包中获取Option的内容的所有权。
impl<T> Linked<T> {
    fn iter(&self) -> LinkedIter<T> {
        LinkedIter { node:
            self.head.as_ref().map(|b: &Box<Node<T>>| &**b)
        }
    }
}

不幸的是,闭包中创建的引用不能超出闭包的生命周期,因为它引用了作为参数传递给闭包的某些内容。编译器会报错(稍微改述一下):

错误:由于冲突要求,无法推断借用表达式的合适生命周期:

首先,生命周期不能超出匿名生命周期#1的定义[在闭包中]

但是,生命周期必须对[iter函数的生命周期]有效

以使[节点:]表达式可分配

我该如何向编译器解释引用在闭包结束后仍将保持有效?

(Playground)


1
我正在Rust Playground上设置一个最小示例。您的代码确实无法编译,但不是您描述的方式(至少,这不是出现的第一个错误)。无论如何,这是链接,它应该提供一个很好的起点来解决您的具体问题:http://is.gd/HleNDJ - mdup
以下是觸發錯誤的最簡單範例:http://is.gd/laiDuZ - mdup
哎呀,我在将代码移动到SO时忘记了生命周期说明符是必需的,所以把它们从代码中删除了。让我重新添加回去,谢谢。第二个确实无法按照我所询问的方式运行。感谢提供MWE链接。 - George Hilliard
1个回答

4

这个方法是告诉编译器,在使用map()函数时,引用的生命周期与迭代器相同。以下是实现代码:

impl<T> Linked<T> {
    fn iter<'a>(&'a self) -> LinkedIter<T> {
        LinkedIter { node:
            self.head.as_ref().map(|b: &'a Box<Node<T>>| &**b)
        }
    }
}

明白了。真的可以说引用的生命周期与 Linked(在此方法中为 self)一样长吗?例如,由 head 拥有的 Node 可能会被删除,从而使迭代器失效吗? - George Hilliard
2
这是不可能的,因为如果你持有一个迭代器 LinkedIter,那么意味着你借用了所有者 Linked(或者你提到的 head)的一部分来填充它。因此,借用检查器会拒绝任何试图修改 Linked 的尝试,直到你释放 LinkedIter - mdup
明白了,这很有道理。 - George Hilliard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接