Разделение изменяемых заимствований для трейта с параметром времени жизни - PullRequest
2 голосов
/ 07 мая 2020
• 1000 Я пытался решить, как определить черту для узлов кода, которые являются операторами или выражениями. Эта черта должна была использоваться для взаимного обхода кода (для целей переписывания). Абстракция, которую я пытался создать, была «узлом кода», который может иметь любое количество дочерних элементов, которые являются операторами или выражениями. Вот как это было:
// 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, поэтому, возможно, Мне не хватает более высокоуровневого решения исходной проблемы.

1 Ответ

1 голос
/ 07 мая 2020

Ваше время жизни '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() {
        // ...
    }
}

К сожалению, мне пришлось использовать нестабильную функцию generi c связанных типов , но я считаю, что это то, что вам нужно. Я также хочу express, что итерация по изменяемым ссылкам может быть не очень хорошей идеей и, возможно, вам следует изменить структуру своей программы, если это возможно.

EDIT:

@ pretzelhammer предложил в комментариях следующую ссылку, которая может быть интересной: Решение проблемы обобщенного потокового итератора без gats

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...