У меня есть две реализации функции удаления. Метод 1 не работает, поскольку компилятор сказал, что curr
заимствовано.
В чем разница между while let Some(boxed_node) = curr
и match curr
?
fn delete_method_1(&mut self, val: i32) {
let mut curr = &mut self.head;
while let Some(boxed_node) = curr {
if boxed_node.val == val {
*curr = boxed_node.next.take(); // it does not work
} else {
curr = &mut boxed_node.next
}
}
}
while let Some(boxed_node) = curr {
| ---------- `*curr` is borrowed here
26 | if boxed_node.val == val {
27 | *curr = boxed_node.next.take(); // it does not work
| ^^^^^
| |
| `*curr` is assigned to here but it was already borrowed
| borrow later used here
fn delete_method_2(&mut self, val: i32) {
let mut curr = &mut self.head;
loop {
match curr {
None => return,
Some(boxed_node) if boxed_node.val == val => {
*curr = boxed_node.next.take();
},
Some(boxed_node) => {
curr = &mut boxed_node.next;
}
}
}
}
Я ожидал, что метод 1 сработает.
Я думаю, что это очень актуально: Почему условное присвоение совпадающей изменяемой ссылки вызывает ошибки заимствования? Возможно, это не дубликат, но он должен дать некоторое представление о поведении компилятора в аналогичном сценарии.
Пожалуйста, опубликуйте Минимально воспроизводимый пример, то есть фрагмент кода, который мы можем скопировать в наши редакторы (или на игровую площадку), и он скомпилируется.
На самом деле разницы между while let
и match
нет. Если бы вы написали второй метод точно так же, как первый, он выглядел бы так:
fn delete_method_2(&mut self, val: i32) {
let mut curr = &mut self.head;
loop {
match curr {
None => return,
Some(boxed_node) => {
if boxed_node.val == val {
*curr = boxed_node.next.take();
} else {
curr = &mut boxed_node.next;
}
}
}
}
}
И вы получите ту же самую ошибку.
То, что происходит на самом деле, немного тонко. Прежде всего, эргономика матча автоматически отменяет для вас участников проверки матча. Что на самом деле происходит это:
fn delete_method_2(&mut self, val: i32) {
let mut curr = &mut self.head;
loop {
// v Rust inserts this dereference...
match *curr {
None => return,
// vvvvvvv and this borrow automatically
Some(ref mut boxed_node) => {
if boxed_node.val == val {
*curr = boxed_node.next.take();
} else {
curr = &mut boxed_node.next;
}
}
}
}
}
Версия while let
выглядит почти так же. Проблема здесь в том, что строка curr = &mut boxed_node.next
заставляет boxed_node
быть заимствованной на всю жизнь curr
. С поддельными прижизненными аннотациями:
fn delete_method_2(&mut self, val: i32) {
let mut curr = &'curr mut self.head;
loop {
match *curr {
None => return,
Some(ref 'curr mut boxed_node) => {
if boxed_node.val == val {
*curr = boxed_node.next.take();
} else {
// we force the compiler to match this lifetime
// vvvvv exactly
curr = &'curr mut boxed_node.next;
}
}
}
}
}
Итак, поскольку boxed_node
необходимо заимствовать для 'curr
, нам не разрешено назначать *curr
в той же области действия. Средство проверки заимствований (пока) не может определить, что двум ветвям необходимо заимствовать boxed_node
на разные сроки жизни.
Однако если вы разделите случаи на две отдельные ветви соответствия, компилятор сможет определить соответствующие времена жизни, которые нужно заимствовать boxed_node
для каждого случая:
fn delete_method_2(&mut self, val: i32) {
let mut curr = &'curr mut self.head;
loop {
match *curr {
None => return,
Some(ref '_ mut boxed_node) if boxed_node.val == val => {
*curr = boxed_node.next.take();
}
Some(ref 'curr mut boxed_node) => {
curr = &'curr mut boxed_node.next;
}
}
}
}
В первом случае компилятору просто нужно заимствовать boxed_node
на время вызова take
, после чего curr
больше не считается заимствованным и мы можем его переназначить.
Во втором случае компилятор может заимствовать boxed_node
на всю жизнь 'curr
.
Вероятно, в этом объяснении есть множество технических ошибок, но в целом, я думаю, именно это и происходит.
Было бы неплохо увидеть остальную часть сообщения об ошибке