【问题标题】:Use reference to new struct in the struct initializer在结构初始化器中使用对新结构的引用
【发布时间】:2019-03-30 21:21:50
【问题描述】:

我正在尝试在 Rust 中创建一个不相交的集合结构。看起来是这样的

struct DisjointSet<'s> {
    id: usize,
    parent: &'s mut DisjointSet<'s>,
}

默认的不相交集是一个单例结构,其中父级引用自身。因此,我希望可以选择执行以下操作:

let a: DisjointSet = DisjointSet {
    id: id,
    parent: self,
};

self 是对将要创建的对象的引用。

我已尝试通过创建自定义构造函数来解决此问题。但是,我的尝试失败了,因为不允许部分初始化。编译器建议使用Option&lt;DisjointSet&lt;'s&gt;&gt;,但这很丑陋。你有什么建议吗?

我的问题不同于Structure containing fields that know each other 因为我有兴趣获取对将要创建的结构的引用。

【问题讨论】:

  • 您是否尝试过创建一个 new 函数来构造一个实例,然后在返回之前对其进行修补?
  • &amp;mut 无论如何都不会对父引用起作用,因为它不允许别名。 &amp; 允许这样做,但仍然需要一些体操(如果您想在构建后更改 id,还需要一个 Cell)。它还对所有DisjointSets 的集合提出了奇怪的要求(例如,不能有Vec&lt;DisjointSet&lt;'s&gt;&gt;)。一个不相交的数据结构实际上是一个 DAG,即一个图,而图在 Rust 中编码很麻烦。我倾向于通过索引推荐传统的实现方式(parent: usize,隐含地指其他地方的Vec&lt;DisjointSet&gt;)。

标签: rust


【解决方案1】:

正如@delnan 所说,这些数据结构的核心是有向无环图 (DAG),其中包含所有共享。 Rust 对可能发生的共享非常严格,因此在这种情况下需要花费一些额外的努力来说服编译器接受您的代码。

幸运的是,“所有需要的共享”并不是字面上的“所有共享”:DAG 是非循环(模希望有parent: self),所以引用计数类型像RcArc 是处理共享的完美方式(如果有循环,引用计数就不那么好了)。具体来说:

struct DisjointSet {
    id: Cell<usize>,
    parent: Rc<DisjointSet>,
}

对于这么小的类型,Cell 的运行时开销为零(肯定有一些语法开销)。

不幸的是,由于编译器建议使用Option&lt;...&gt; 的相同原因,这仍然不太正确。无法创建第一个 DisjointSet。但是,建议的修复仍然有效:

struct DisjointSet {
    id: Cell<usize>,
    parent: Option<Rc<DisjointSet>>,
}

(Option&lt;...&gt; 是免费的:Option&lt;Rc&lt;...&gt;&gt; 是单个可空指针,就像 Rc&lt;...&gt; 是单个不可空指针一样,可能需要在“我是否有父级”上进行分支无论如何。)

如果您打算采用这种方法,我建议不要尝试使用 Option 进行部分初始化,而是使用它来表示给定集合是“根”这一事实。用这种表示很容易遍历一条链,例如

fn find_root(mut x: &DisjointSet) -> &DisjointSet {
    while let Some(ref parent) = x.parent {
        x = parent
    }
    x
}

同样的方法应该适用于引用,但生命周期通常很难兼顾。

【讨论】:

    猜你喜欢
    • 2013-01-25
    • 1970-01-01
    • 2016-03-20
    • 1970-01-01
    • 2016-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多