Невозможно получить изменяемую ссылку при итерации рекурсивной структуры: нельзя заимствовать как изменяемый более одного раза за раз
Я пытаюсь перемещаться по рекурсивной структуре данных итеративно, чтобы вставить элементы в определенную позицию. Насколько я понимаю, это означает, что нужно взять изменяемую ссылку на корень структуры и последовательно заменить ее ссылкой на своего последователя:
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
}
}
(Ржавчина детская площадка ссылка)
Тем не менее, это не удается:
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()
быть правильно реализованным с помощью Safe Rust?
4 ответа
Это возможно... но мне бы хотелось иметь более элегантное решение.
Хитрость не в том, чтобы позаимствовать у 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
, См. Соответствующий пост в блоге Stuff, который выполняет функция идентификации (в Rust).
Исходный код работает как есть, если включены нелексические времена жизни:
#![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
из-за недавних языковых изменений.
Вы можете использовать рекурсию, чтобы удовлетворить заемщика. Недостатком этого является создание стекового фрейма для каждого элемента в вашем списке. Если ваш список длинный, это обязательно приведет к переполнению стека. 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())
}
}
Оно работает:
fn back(&mut self) -> &mut Link {
let mut anchor = &mut self.root;
while anchor.is_some(){
anchor = &mut {anchor}.as_mut().unwrap().next;
}
anchor
}