https://intoraw.xyz/blog/feed.xml

Fight with Rust borrow checker by IntoParts

2024-08-28

在 rust 程序中,一个结构如果由多个子结构构成例如

pub struct Foo {
  a: FieldA,
  b: FieldB,
  c: FieldC,
}

如果想修改某个子结构,例如修改Foo.a,就需要拿到 Foo 的可变引用,因为可变引用的唯一性,会导致同时修改Foo.aFoo.b带来compiler 的报错。尤其是当修改逻辑变得非常复杂的时候。

在 rust 优秀的代码中我们经常看到一种方法来避免获取可变引用,就是IntoPartsFromParts。 简单来说,我们可以先拿到 Foo 的所有权,然后调用 IntoParts,将 Foo 拆分为FieldA, FieldB,FieldC三个拥有独立所有权的变量,然后调用修改逻辑。 最终通过 FromParts 来把 Foo 重新组装起来。

DataFusion

在 DataFusion 中,optimizer 经常会对 plannode 进行 rewrite,如果拿到的是 plannode 的可变引用,这个可变引用需要在 tree 的遍历过程中始终保持。 为了避免和borrow checker 斗智斗勇,datafusion 定义了 ConcretTreeNode 的 trait 如下。

pub trait ConcreteTreeNode: Sized {
    /// Provides read-only access to child nodes.
    fn children(&self) -> &[Self];

    /// Detaches the node from its children, returning the node itself and its detached children.
    fn take_children(self) -> (Self, Vec<Self>);

    /// Reattaches updated child nodes to the node, returning the updated node.
    fn with_new_children(self, children: Vec<Self>) -> Result<Self>;
}

先将 node 拆分为 self 和 children,最终再重新组装。