Невозможно получить изменяемую ссылку при повторении рекурсивной структуры: не может заимствовать как изменчивый более одного раза за раз

Я пытаюсь перемещаться по рекурсивной структуре данных итеративно, чтобы вставлять элементы в определенную позицию. К моему ограниченному пониманию, это означает, что вы используете изменяемую ссылку на корень структуры и последовательно заменяете ее ссылкой на ее последователя:

type Link = Option<Box<Node>>;

struct Node {
    next: Link
}

struct Recursive {
    root: Link
}

impl Recursive {
    fn back(&mut self) -> &mut Link {
        let mut anchor = &mut self.root;
        while let Some(ref mut node) = *anchor {
            anchor = &mut node.next;
        }
        anchor
    }
}

(Ссылка на площадку Rust)

Однако это не удается:

error[E0499]: cannot borrow 'anchor.0' as mutable more than once at a time
  --> src/main.rs:14:24
   |
14 |         while let Some(ref mut node) = *anchor {
   |                        ^^^^^^^^^^^^
   |                        |
   |                        second mutable borrow occurs here
   |                        first mutable borrow occurs here
...
18 |     }
   |     - first borrow ends here

error[E0506]: cannot assign to 'anchor' because it is borrowed
  --> src/main.rs:15:13
   |
14 |         while let Some(ref mut node) = *anchor {
   |                        ------------ borrow of 'anchor' occurs here
15 |             anchor = &mut node.next;
   |             ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed 'anchor' occurs here

error[E0499]: cannot borrow '*anchor' as mutable more than once at a time
  --> src/main.rs:17:9
   |
14 |         while let Some(ref mut node) = *anchor {
   |                        ------------ first mutable borrow occurs here
...
17 |         anchor
   |         ^^^^^^ second mutable borrow occurs here
18 |     }
   |     - first borrow ends here

Это имеет смысл, поскольку и anchor и node ссылаются на одну и ту же структуру, но я действительно не забочусь о anchor больше после его разрушения.

Как можно корректно выполнить back() корректную работу с использованием надежной ржавчины?

Ответы

Ответ 1

Это возможно... но мне жаль, что у меня не было более элегантного решения.

Трюк НЕ заимствовать у anchor и, следовательно, жонглировать между двумя аккумуляторами:

  • один из которых содержит ссылку на текущий узел
  • другому назначается ссылка на следующий узел

Это приводит меня к:

impl Recursive {
    fn back(&mut self) -> &mut Link {
        let mut anchor = &mut self.root;

        loop {
            let tmp = anchor;
            if let Some(ref mut node) = *tmp {
                anchor = &mut node.next;
            } else {
                anchor = tmp;
                break;
            }
        }

        anchor
    }
}

Не совсем красиво, но это что-то, что может проверить заемщик ¯\_ (ツ) _/¯.

@ker улучшил это, создав неназванное временное:

impl Recursive {
    fn back(&mut self) -> &mut Link {
        let mut anchor = &mut self.root;

        loop {
            match {anchor} {
                &mut Some(ref mut node) => anchor = &mut node.next,
                other => return other,
            }
        }
    }
}

Трюк здесь заключается в том, что использование {anchor} перемещает содержимое anchor в неназванное временное, на котором выполняется совпадение. Поэтому в match блоке мы не заимствуем из anchor а из временного, оставляя нас свободными изменять anchor. См. Связанное сообщение в блоге. Функция идентификатора (в ржавчине).

Ответ 2

Вы можете использовать рекурсию для выполнения проверки чека. Это имеет недостаток в создании фрейма стека для каждого элемента в вашем списке. Если ваш список длинный, это, безусловно, приведет к переполнению стека. LLVM оптимизирует метод Node::back в цикле (см. LLVM IR, сгенерированный на игровой площадке)

impl Node {
    fn back(&mut self) -> &mut Link {
        match self.next {
            Some(ref mut node) => node.back(),
            None => &mut self.next,
        }
    }
}

impl Recursive {
    fn back(&mut self) -> Option<&mut Link> {
        self.root.as_mut().map(|node| node.back())
    }
}

Ответ 3

Исходный код работает как-то, когда включены нелегические времена жизни:

#![feature(nll)]

type Link = Option<Box<Node>>;

struct Node {
    next: Link
}

struct Recursive {
    root: Link
}

impl Recursive {
    fn back(&mut self) -> &mut Link {
        let mut anchor = &mut self.root;
        while let Some(node) = anchor {
            anchor = &mut node.next;
        }
        anchor
    }
}

fn main() {}

Нелексические времена жизни повышают точность проверки счетчика компилятора, позволяя ему видеть, что изменчивый заимствование anchor больше не используется. Мы также можем упростить ключевые слова в if let из-за недавних изменений языка.

Ответ 4

Оно работает:

fn back(&mut self) -> &mut Link {
    let mut anchor = &mut self.root;
    while anchor.is_some(){
        anchor = &mut {anchor}.as_mut().unwrap().next;
    }
    anchor
}