【发布时间】:2020-02-11 21:43:11
【问题描述】:
我正在尝试为树遍历定义一个类似于链表的递归结构。一个节点有一些数据并且可以访问它的父节点。子节点应该可变地借用它的父节点以确保独占访问,并在它被删除后释放它。我可以使用不可变引用来定义这个结构,但是当我使父引用可变时不能。在使父引用可变时,我被编译器错误弄糊涂了,不明白。
如何为这种具有可变父引用的递归结构定义生命周期?
这是一个最小的例子。这会编译但使用只读引用:
struct Node<'a> {
// Parent reference. `None` indicates a root node.
// I want this to be a mutable reference.
pub parent: Option<&'a Node<'a>>,
// This field just represents some data attached to this node.
pub value: u32,
}
// Creates a root node
// I use a static lifetime since there's no parent for the root so there are no constraints there
fn root_node(value: u32) -> Node<'static> {
Node {
parent: None,
value,
}
}
// Creates a child node
// The lifetimes indicates that the parent must outlive its child
fn child_node<'inner, 'outer: 'inner>(
parent: &'inner mut Node<'outer>,
value: u32,
) -> Node<'inner> {
Node {
parent: Some(parent),
value,
}
}
// An example function using the struct
fn main() {
let mut root = root_node(0);
let mut c1 = child_node(&mut root, 1);
let mut c2 = child_node(&mut c1, 2);
{
let mut c3 = child_node(&mut c2, 3);
let c4 = child_node(&mut c3, 4);
let mut cur = Some(&c4);
while let Some(n) = cur {
println!("{}", n.value);
cur = n.parent;
}
}
{
let c5 = child_node(&mut c2, 5);
let mut cur = Some(&c5);
while let Some(n) = cur {
println!("{}", n.value);
cur = n.parent;
}
}
println!("{}", c2.value);
}
Rust playground: immutable reference
我想要一个可变引用,所以我尝试替换 Node 结构以使用可变引用:
struct Node<'a> {
// Parent reference. `None` indicates a root node.
// I want this to be a mutable reference.
pub parent: Option<&'a mut Node<'a>>,
// This field just represents some data attached to this node.
pub value: u32,
}
然后我收到以下错误:
error[E0623]: lifetime mismatch
--> src/main.rs:25:22
|
21 | parent: &'inner mut Node<'outer>,
| ------------------------
| |
| these two types are declared with different lifetimes...
...
25 | parent: Some(parent),
| ^^^^^^ ...but data from `parent` flows into `parent` here
Rust playground: mutable reference
我不明白可变性与流入字段的数据之间的关系。在不可变的情况下,我已经要求函数传递可变/独占引用。我一直在尝试各种生命周期组合(使用单一生命周期、反转它们的关系等),但没有成功。
【问题讨论】:
-
很确定你的整个概念是有缺陷的。这种结构将允许可变别名(
head.next.next和(head.next).next)。 -
@Shepmaster 我的目标是只有最低(活动)节点才能操作链中的数据。我不太了解您对可变别名的表示法。在我的示例中,一旦创建了
c3,它就会借用c2(以及c1和root),因此在删除c2之前您无法访问它们,因此您不应该使用别名他们(至少这是目标)。 -
这让我想起了How do I implement the Chain of Responsibility pattern using a chain of trait objects?,但我不确定它是否足够接近以标记重复。这个问题的答案(我的和 Lukas 的)能说明你的问题吗?
-
@trentcl 谢谢,我会调查的。为了提供更多上下文,我想使用这个结构在递归函数调用之间传递数据。每个节点代表一个函数调用级别。这确保首先创建父节点,我将其作为可变引用传递给下一次调用,它创建一个对该父节点具有独占访问权限的子节点,当内部调用结束时,子节点被删除,我重新获得对父节点。
-
@trentcl 您发布的问题确实相似。这实际上是一个使用特征的更普遍的问题,下一个节点是用方法设置的,而不是在构造时设置的。您对这个问题的回答实际上对应于我的不可变案例。问题是我需要可变性。尽管如此,我相信它帮助我解决了这个问题。我觉得这与终身差异有关。我想我记得结构中的不可变引用是协变的,但可变引用是不变的,但我不确定这是否意味着我的问题是可解决的。
标签: rust lifetime recursive-datastructures