Tree 有没有一种方法可以在不增加运行时开销的情况下构建具有循环链接的结构?

Tree 有没有一种方法可以在不增加运行时开销的情况下构建具有循环链接的结构?,tree,rust,cyclic-reference,cyclic-graph,Tree,Rust,Cyclic Reference,Cyclic Graph,我正在尝试在Rust中实现循环链接数据结构。我的节点定义为: #[derive(Debug)] enum Node<'a> { Link(&'a Node<'a>), Leaf, } 这是可行的,但现在我不知道如何用link2的引用替换占位符来“关闭循环”。我尝试了这一点,但它不起作用,因为我无法分配到借用了上一行的link1,而且link2将超出范围,而link1仍然引用它: let placeholder = Node::Leaf; {

我正在尝试在Rust中实现循环链接数据结构。我的
节点
定义为:

#[derive(Debug)]
enum Node<'a> {
    Link(&'a Node<'a>),
    Leaf,
}
这是可行的,但现在我不知道如何用
link2
的引用替换占位符来“关闭循环”。我尝试了这一点,但它不起作用,因为我无法分配到借用了上一行的
link1
,而且
link2
将超出范围,而
link1仍然引用它:

let placeholder = Node::Leaf;
{
    let mut link1 = Node::Link(&placeholder);
    {
        let link2 = Node::Link(&link1);
        link1 = Node::Link(&link2);
        println!("{:?}", link2);
    } // link2 gets dropped
} // link1 gets dropped
错误:`link2`的寿命不够长
-->src/main.rs:15:9
|
13 | link1=节点::Link(&link2);
|----借用发生在这里
14 | println!(“{:?}”,链接2);
15 |}//link2被丢弃
|^`link2`仍在借用时放在此处
16 |}//link1被丢弃
|-借来的价值需要一直存在到这里
错误[E0506]:无法分配给'link1',因为它是借用的
-->src/main.rs:13:13
|
12 |让link2=Node::Link(&link1);
|----link1的借词出现在这里
13 | link1=节点::Link(&link2);
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^借来的'link1'的赋值发生在此处
我可以尝试通过使用
Rc
s来避免这些生命周期问题,但这听起来似乎会破坏Rust的0运行时成本生命周期管理的目的

将所有节点放入
Vec
并仅直接引用该节点的另一次尝试也不起作用:

use std::ops::IndexMut;

let mut store = Vec::new();
store.push(Node::Leaf);
store.push(Node::Link(&store[0]));
store.push(Node::Link(&store[1]));
*store.index_mut(1) = Node::Link(&store[2]);
因为我无法在不可变借用节点的情况下更改任何节点,但它们都已被永久借用

error[E0502]:无法将'store'作为不可变项借用,因为它也是作为可变项借用的
-->src/main.rs:12:28
|
12 | store.push(节点::链接(&store[0]);
|-----^^^^-可变借阅结束于此
|     |                      |
||此处发生不可变借用
|可变借用发生在这里
错误[E0502]:无法将'store'作为可变项借用,因为它也是作为不可变项借用的
-->src/main.rs:13:5
|
12 | store.push(节点::链接(&store[0]);
|----此处发生不可变借用
13 | store.push(节点::链接(&store[1]);
|^^^^^此处发生可变借用
14 |*store.index_mut(1)=节点::链接(&store[2]);
15 | }
|-不可变的借阅到此结束
错误[E0502]:无法将'store'作为不可变项借用,因为它也是作为可变项借用的
-->src/main.rs:13:28
|
13 | store.push(节点::链接(&store[1]);
|-----^^^^-可变借阅结束于此
|     |                      |
||此处发生不可变借用
|可变借用发生在这里
错误[E0502]:无法将'store'作为可变项借用,因为它也是作为不可变项借用的
-->src/main.rs:14:6
|
12 | store.push(节点::链接(&store[0]);
|----此处发生不可变借用
13 | store.push(节点::链接(&store[1]);
14 |*store.index_mut(1)=节点::链接(&store[2]);
|^^^^^此处发生可变借用
15 | }
|-不可变的借阅到此结束
有没有一种方法可以使这种结构具有循环链接而不需要运行时开销,例如像我尝试的那样使用纯引用?我对额外的内存开销没有意见,比如支持
Vec
持有所有节点的所有权

有没有一种方法可以使这种结构具有循环链接而不需要运行时开销,例如像我尝试的那样使用纯引用?我对额外的内存开销没什么意见,比如支持Vec拥有所有节点的所有权

是的,有很多方法

然而,认识到Rust需要始终执行混叠异或可变性原则是非常重要的。最好在编译时强制执行它,以使运行时成本为0,但是有时需要在运行时管理它,并且有多种结构:

  • Cell
    RefCell
    AtomicXXX
    Mutex
    RWLock
    (名称不正确)是关于安全地变异别名项
  • Rc
    Weak
    Arc
    ,都是关于拥有多个所有者的
平衡潜在的运行时开销与获得的灵活性是一门艺术;这需要一些经验和实验


在您的特定情况下(使用相互引用的节点构建BNF语法),我们确实可以使用
Cell
实现0运行时开销

然而,主要的困难是获得一组具有相同生存期的节点。您使用
Vec
的想法是正确的,但正如您所注意到的,只要借用一个节点,就不能再次变异
Vec
:这是因为增加
Vec
可能会导致它重新分配其底层存储,从而使已获得的引用悬空(指向已释放的内存)

一般的解决方案是使用不安全的代码自己管理节点的生命周期。然而,您是幸运的:正如您所提到的,您的案例是特殊的,因为节点的数量是有界的(由语法定义决定),并且您可以一次删除所有节点。这需要一个竞技场

因此,我的建议有两方面:

  • 将节点隐藏在
  • 用于可变部分(
    &T
    is
    Copy

  • 如果没有
    不安全的
    代码,您将无法将竞技场存储在与语法其余部分相同的结构中;由您决定是使用
    不安全的
    还是构建您的程序,以使竞技场能够在计算过程中生存。

    您计划如何使用此数据结构?节点将保存什么样的数据?您计划如何为它强制执行Rust的别名XOR可变性原则?如果你是c
    use std::ops::IndexMut;
    
    let mut store = Vec::new();
    store.push(Node::Leaf);
    store.push(Node::Link(&store[0]));
    store.push(Node::Link(&store[1]));
    *store.index_mut(1) = Node::Link(&store[2]);