简体   繁体   中英

Separating mutable borrows for trait with lifetime parameter

I ran into an issue while trying to define and use a trait with methods that borrow self mutably.

Some context that might make it easier: I am working on a toy compiler, and the problem I was trying to solve was to define a trait for code nodes, which are either statements or expressions. The trait was meant to be used for traversing code mutably (for rewriting purposes). The abstraction I was trying to create was a "code node" that may have any number of children that are either statements or expressions. This is how it went:

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

This trait would be then implemented for quite a few types (I have a separate type for different kinds of statements and expressions).

The way I tried to use it was:

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

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

And this is where the problem lies. Rust compiler treats a call to node.child_stmts as a mutable borrow of node for the entire lifetime 'a , and so it does not allow a call to node.child_exprs later in the same function. Here is how the error looks:

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

What I want to do is to somehow make compiler aware of the fact that node implements CodeNode<'a> for any lifetime parameter, and so it should use two separate lifetimes for two calls, but I can't quite figure out a way to do it.

Any suggestions are welcome, I don't have a lot of experience with Rust, so maybe I am missing some more high-level solution to the original problem.

Your lifetime 'a is constrained by the CodeNode so both functions will be called with the same lifetime, but what you want are two lifetimes constrained by the two functions. So why not do something like this.

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() {
        // ...
    }
}

Unfortunately I had to use the unstable feature of generic associated types , but I believe this is what you want. I also want to express that iterating over mutable references might not be a good idea and maybe you should change your program structure if that is possible.

EDIT:

@pretzelhammer proposed in the comments the following link which might be interesting: Solving the generalized streaming iterator problem without gats

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM