У меня есть две реализации функции удаления. Метод 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.
Вероятно, в этом объяснении есть множество технических ошибок, но в целом, я думаю, именно это и происходит.
Было бы неплохо увидеть остальную часть сообщения об ошибке