Разное поведение для одного и того же матча и блоков if let

Я пытаюсь понять, чем я обидел компилятор ржавчины, но не могу понять, что не так.

Итак, основной алгоритм:

  1. Получите изменяемую ссылку на список.
  2. Если следующий узел имеет значение NULL, выделите и присвойте указатель следующего узла; перерыв
  3. Если следующий узел не равен нулю, получить изменяемую ссылку на следующий узел; перейти к 1

И вот как я это закодировал

fn append_at_end(&mut self, value: i32) {
    match self {
        List::Empty => {
            *self = List::new_node(value);
        },
        List::Head(head) => {
            let mut itr = head;
            loop {
                if let Some(x) = itr {
                    if x.next.is_none() {
                        x.next = Some(Node::new_node(value));
                        break;
                    } else {
                        itr = &mut x.next;
                    }
                }
            }
        }
    }
}

Теперь компилятор ржавчины жалуется на этот блок кода.

   Compiling random v0.1.0 (/Users/katharva/Personal/rust/random)
error[E0503]: cannot use `*itr` because it was mutably borrowed
  --> src/main.rs:65:28
   |
65 |                     if let Some(x) = itr {
   |                            ^^^^^-^
   |                            |    |
   |                            |    `itr.0` is borrowed here
   |                            use of borrowed `itr.0`
   |                            borrow later used here

error[E0499]: cannot borrow `itr.0` as mutable more than once at a time
  --> src/main.rs:65:33
   |
65 |                     if let Some(x) = itr {
   |                                 ^    --- first borrow used here, in later iteration of loop
   |                                 |
   |                                 `itr.0` was mutably borrowed here in the previous iteration of the loop

Some errors have detailed explanations: E0499, E0503.
For more information about an error, try `rustc --explain E0499`.
error: could not compile `random` (bin "random") due to 2 previous errors

Но тот же код, написанный как

fn append_at_end_option2(&mut self, value: i32) {
    match self {
        List::Empty => {
            *self = List::new_node(value);
        },
        List::Head(head) => {
            let mut itr = head;
            loop {
                match itr {
                    None => {}, 
                    Some(x) => {
                        // A bit convoluted but shows the bug
                        if x.next.is_none() {
                            x.next = Some(Node::new_node(value));
                            break;
                        } else {
                            itr = &mut x.next;
                        }
                    }
                }
            }
        }
    }
}

Или

fn append_at_end_option1(&mut self, value: i32) {
    match self {
        List::Empty => {
            *self = List::new_node(value);
        },
        List::Head(head) => {
            let mut itr = head;
            loop {
                if let Some(x) = itr {
                    itr = &mut x.next;
                    continue;
                } 
                
                *itr = Some(Node::new_node(value));
                break;
            }
        }
    }
}

все в порядке, компилятор этим доволен. И append_at_end_option2, и append_at_end_option1 будут работать как положено. Насколько я понимаю, все три части кода имеют одинаковую логику. Так почему же первый не приемлем?

Добавленная вами ссылка ведет на пустую игровую площадку. Вам нужно использовать кнопку [ПОДЕЛИТЬСЯ] в правом верхнем углу, чтобы получить ссылку, которой можно поделиться. Также убедитесь, что минимальный воспроизводимый пример находится в вашем вопросе, просто ссылки на него недостаточно!

cafce25 01.08.2024 09:14

Если вы нашли решение, добавьте ответ ниже, не редактируйте свой вопрос, чтобы он содержал такие вещи, как решение или «закрыто».

cafce25 01.08.2024 09:17
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
3
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Одно из различий между выражением if let и match заключается в том, что if let становится владельцем значения. и заимствование его заново привело бы к ошибке. Насколько я знаю, причина, по которой работает третий метод, заключается в используемой вами операции Deref, которая освобождает значение.

Это неправильно, match и if let семантически эквивалентны, оба могут стать владельцем и оба могут получить ссылку.

cafce25 01.08.2024 11:17
Ответ принят как подходящий

Судя по всему, это работает на игровой площадке ржавчины. Похоже на ошибку компилятора, которую исправили.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fc48a275e5f34536658727c9a6b097af

Другие вопросы по теме