【问题标题】:Rust vector of struct instances with (non-circular) references to each other具有(非循环)相互引用的结构实例的 Rust 向量
【发布时间】:2022-10-23 02:54:33
【问题描述】:

我想创建一个TestStruct 的向量。 TestStruct 具有对另一个 TestStruct 实例的可选引用。没有TestStruct 将永远引用自己,也不会有预期用途的循环引用。多个others 可以引用同一个TestStructTestStruct 实例不需要变异。

是否可以使用引用来表达这一点,还是我需要RcWeak

struct TestStruct<'a>
{
    other: Option<&'a TestStruct<'a>>
}

fn testFn()
{
    let mut events = vec![TestStruct{other: None}];
    events.push(TestStruct{other: Some(&events[0])});
}

产量:

error[E0502]: cannot borrow `events` as mutable because it is also borrowed as immutable
 --> src\test.rs:9:5
  |
9 |     events.push(TestStruct{other: Some(&events[0])});
  |     ^^^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^------^^^^^^
  |     |      |                            |
  |     |      |                            immutable borrow occurs here
  |     |      immutable borrow later used by call
  |     mutable borrow occurs here

例如,我可以通过创建Box&lt;TestStruct&gt; 的向量来使其工作吗?或者对一个框在向量中的 TestStruct 的引用是否也会隐式借用该向量?

【问题讨论】:

    标签: rust reference reference-counting


    【解决方案1】:

    如果我们接受一些限制,

    • 实际上并未使用Vec
    • 创建结构后无法移动结构

    那么这可以通过typed_arena::Arena 来完成:

    // [dependencies]
    // typed-arena = "2.0.1"
    use typed_arena::Arena;
    
    struct TestStruct<'a> {
        other: Option<&'a TestStruct<'a>>,
    }
    
    pub fn test_fn() {
        let events = Arena::new();
        let first = &*events.alloc(TestStruct { other: None });
        let second = &*events.alloc(TestStruct { other: Some(first) });
    }
    

    Arena 提供的主要区别在于 Arena::alloc() 采用 &amp;self — 表明只有一个共享需要引用竞技场来分配(添加元素)。因此,Rust 的可变性规则告诉我们,即使我们 alloc() 更多,我们也可以继续使用返回的引用,而 Vec::push() 使所有现有引用无效(因为它必须,因为向量的项目可能被移动到重新分配的内存中以增长向量) .

    然而,所有这些参考仍然是竞技场的借用。因此,竞技场被有效地固定了——例如,你不能把整个数据结构和返回它来自一个函数;您必须在创建它的范围内使用它,或者在嵌套范围或函数调用中使用它。这些限制基本上与您通过尾递归函数实现算法相同 - 也就是说,该函数可以将每个 TestStruct 放入一个 let 变量中,然后使用对它的引用调用自身,以累积一个一组可以引用旧值的值。

    还有其他方法可以在 Rust 中创建“自引用”数据结构,例如 ouroboros,但据我所知,它们都不支持创建任意深度的可移动图。如果您需要在创建后能够返回该结构,那么您应该使用RcArc,或诸如petgraph 提供的显式图形数据结构。


    通过Box 添加间接引用并不能帮助允许这种引用结构,原因有两个:

    1. Box 暗示指向其内容的指针的唯一性,其方式与直接所有权或&amp;mut 完全相同。就语言规则而言,移动Box&lt;T&gt;&amp;T 的影响与移动T 本身的含义完全相同。 (ouroboros 通过创建 Box-like 来工作才不是以这种方式断言唯一性。)

    2. 借用检查器中没有任何内容可以执行分析,这证明了直到对它的引用之后才会删除该框.例如,假设Vec&lt;Box&lt;TestStruct&gt;&gt; 被允许;这意味着TestStructs 必须以与其在向量中的位置相反的顺序被删除(否则impl Drop for TestStruct 可能会观察到无效的引用)。但是,Vec 中没有任何内容表明这些项目无法重新排序,因此这还不够。

      你需要一个特殊的数据结构,它只允许安全操作,并以正确的顺序放置(基本上是typed_arenaouroboros 的想法的组合)。原则上,这样的数据结构是可以存在的,但我不知道有人写过。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多