Почему в сопоставлении с образцом кортежа Rust требуется явное заимствование? - PullRequest
0 голосов
/ 10 мая 2018

Я пишу бинарное дерево на 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 Ответ

0 голосов
/ 10 мая 2018

Ключ в том, что self.root и bt.root являются "выражением места" s, а кортеж - нет. Причина того, что работает # 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)), что может быть более удобным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...