Почему в сопоставлении с образцом кортежа Rust требуется явное заимствование?

Я пишу бинарное дерево на Rust, и проверка заимствований действительно смущает меня. Вот минимальный пример, который воспроизводит проблему.

Двоичное дерево определяется следующим образом:

struct NonEmptyNode;

pub struct BinaryTree {
    root: Option<NonEmptyNode>,
}

Код, отклоненный контролером заимствований:

// Implementation #1
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match (self.root, bt.root) {
        (Some(ref rt), Some(ref node)) => {
            setter(rt, node);
            true
        }
        _ => false,
    }
}

Сообщение об ошибке

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:10:16
   |
10 |         match (self.root, bt.root) {
   |                ^^^^ cannot move out of borrowed content

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:10:27
   |
10 |         match (self.root, bt.root) {
   |                           ^^ cannot move out of borrowed content

Чтобы заставить его работать, код должен быть изменен на:

// Implementation #2
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match (&self.root, &bt.root) {
        // explicit borrow
        (&Some(ref rt), &Some(ref node)) => {
            // explicit borrow
            setter(rt, node);
            true
        }
        _ => false,
    }
}

Если я сопоставлю шаблон по одной переменной за раз без явного заимствования, средство проверки заимствования не будет жаловаться вообще:

// Implementation #3
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
    match self.root {
        Some(ref rt) => match bt.root {
            // No explict borrow will be fine
            Some(ref node) => {
                // No explicit borrow will be fine
                setter(rt, node);
                true
            }
            _ => false,
        },
        _ => false,
    }
}

Почему реализация № 3 не требует явного заимствования, а реализация № 1 требует?

1 ответ

Решение

Ключ в том, что self.root а также bt.root являются "выражением места", а кортеж - нет. Причина № 3 в том, что компилятор знает, как "пробиться" через промежуточные выражения, чтобы привязаться к исходному месту хранения.

На это можно посмотреть иначе: очень простые выражения, такие как self.root особенность в том, что они выглядят и ведут себя как значения (и имеют тип значения), но тайно компилятор запоминает, как он достиг этого значения, позволяя ему вернуться назад и получить указатель на то, откуда это значение было прочитано.

Самый простой способ понять, является ли выражение выражением "место", состоит в том, чтобы попытаться присвоить ему значение. Если вы можете сделать expr = some_value;, затем expr должно быть "выражением места". Кстати, это также, почему, когда вы пишете &self.root, вы получите указатель на где self.root хранится, а не указатель на копию self.root,

Этот бизнес "выражения места" не работает для кортежей, потому что у них нет этого свойства. Чтобы создать кортеж, компилятор должен фактически прочитать значения для элементов кортежа и переместить или скопировать их в новое хранилище для кортежа. Это разрушает любые ассоциации мест, которые мог иметь компилятор: эти значения буквально больше не там, где они были раньше.

Наконец, вы можете посмотреть на Option::as_ref, который превращается &Option<T> в Option<&T>, Это позволит вам соответствовать (self.root.as_ref(), bt.root.as_ref())с такими узорами, как (Some(rt), Some(node)), что может быть более удобным.

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