rust - 使用生命周期参数为特征分离可变借位

标签 rust traits lifetime borrow-checker borrowing

我试图通过可变地借用self的方法来定义和使用特征时遇到了一个问题。

某些上下文可能会使它变得更容易:我正在研究一个玩具编译器,而我试图解决的问题是为代码节点定义一个特征,该特征可以是语句或表达式。该特征旨在用于可变地遍历代码(用于重写目的)。我试图创建的抽象是一个“代码节点”,它可能具有任意数量的子代,无论是语句还是表达式。它是这样的:

// 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

我想做的是使编译器意识到node为任何生命周期参数实现CodeNode<'a>的事实,因此它应该为两个生命周期使用两个单独的生命周期
电话,但我还不太想办法。

欢迎提出任何建议,我在Rust方面经验不足,所以也许我缺少一些针对原始问题的高级解决方案。

最佳答案

您的生存期'aCodeNode约束,因此将使用相同的生存期来调用这两个函数,但是您想要的是受这两个函数约束的两个生存期。那么为什么不做这样的事情。

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在评论中建议了以下可能有趣的链接:Solving the generalized streaming iterator problem without gats

关于rust - 使用生命周期参数为特征分离可变借位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61659671/

相关文章:

rust - 为了能够将 `u8`用作向量中的索引,我必须强制转换什么?

rust - Vec 会导致堆栈溢出吗?

rust - 实现具有关联特征类型的特征

jdbc - Derby/JDBC连接生命周期(或空闲超时)

closures - 如何为闭包参数指定生命周期?

rust - 索引框错误的 Vec

singleton - 如何让 Rust 单例的析构函数运行?

PHP 特征方法冲突 : trait "inheritance" and trait hierarchies

smalltalk - 特质是好是坏?

generics - 为什么我需要为不是结构成员的结构的通用参数提供生命周期?