使用生命周期参数分离特征的可变借用

Separating mutable borrows for trait with lifetime parameter

我 运行 在尝试通过可变借用 self 的方法定义和使用特征时遇到了问题。

一些可能使它更容易的上下文:我正在开发一个玩具编译器,我试图解决的问题是为代码节点定义一个特征,这些节点可以是语句也可以是表达式。该特征旨在用于可变地遍历代码(用于重写目的)。我试图创建的抽象是一个 "code node",它可能有任意数量的子项,这些子项要么是语句,要么是表达式。事情是这样的:

// Actually these are enums with different payload types for different kinds of exprs/stmts,
// but this is not relevant.
struct Expression;
struct Statement;

trait CodeNode<'a>
where
    Self::ExprIter: Iterator<Item = &'a mut Expression>,
    Self::StmtIter: Iterator<Item = &'a mut Statement>,
{
    type ExprIter;
    type StmtIter;

    fn child_exprs(&'a mut self) -> Self::ExprIter;
    fn child_stmts(&'a mut self) -> Self::StmtIter;
}

此特征随后会为很多类型实现(我有一个单独的类型用于不同类型的语句和表达式)。

我尝试使用它的方式是:

fn process<'a>(node: &'a mut impl CodeNode<'a>) {
    for _stmt in node.child_stmts() {
        // ...
    }

    for _expr in node.child_exprs() {
        // ...
    }
}

而这正是问题所在。 Rust 编译器将对 node.child_stmts 的调用视为对 node 的可变借用 整个 生命周期 'a,因此它不允许调用node.child_exprs 稍后在同一函数中。错误看起来是这样的:

error[E0499]: cannot borrow `*node` as mutable more than once at a time
  --> src/main.rs:21:18
   |
16 | fn process<'a>(node: &'a mut impl CodeNode<'a>) {
   |            -- lifetime `'a` defined here
17 |     for _stmt in node.child_stmts() {
   |                  ------------------
   |                  |
   |                  first mutable borrow occurs here
   |                  argument requires that `*node` is borrowed for `'a`
...
21 |     for _expr in node.child_exprs() {
   |                  ^^^^ second mutable borrow occurs here

我想做的是以某种方式让编译器意识到 nodeany 生命周期参数实现 CodeNode<'a> 的事实,因此它应该对两个人使用两个独立的生命周期 打电话,但我想不出办法。

欢迎提出任何建议,我对 Rust 没有太多经验,所以我可能缺少对原始问题的一些更高级的解决方案。

您的生命周期 'a 受到 CodeNode 的限制,因此两个函数将以相同的生命周期被调用,但您想要的是两个生命周期受两个函数的限制。那么为什么不做这样的事情呢。

struct Expression;
struct Statement;

trait CodeNode
{
    type ExprIter<'a> : Iterator<Item = &'a mut Expression>; //unstable
    type StmtIter<'a> : Iterator<Item = &'a mut Statement>; //unstable

    fn child_exprs<'a>(&'a mut self) -> Self::ExprIter<'a>;
    fn child_stmts<'a>(&'a mut self) -> Self::StmtIter<'a>;
}

fn process(node: &mut impl CodeNode) {
    for _stmt in node.child_stmts() {
        // ...
    }

    for _expr in node.child_exprs() {
        // ...
    }
}

不幸的是,我不得不使用 generic associated types 的不稳定功能,但我相信这就是您想要的。 我还想表达迭代可变引用可能不是一个好主意,如果可能的话,也许你应该改变你的程序结构。

编辑:

@pretzelhammer 在评论中提出以下 link 这可能很有趣:Solving the generalized streaming iterator problem without gats