Изменяемый и неизменяемый доступ к данным

#rust

#Ржавчина

Вопрос:

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

 struct Node;
impl Node {
    fn children(amp;mut self) -> amp;mut Vec<Node> {
        // Pulls a field out of Node, to be mutated
    }
    fn next_node(amp;self) -> Node {
        // Produces a new value using values computed from self.
        // Doesn't hold a reference to self
    }
}
[...]
if self.children().len() > K {
    let mut next_node = self.next_node();
    let list = self.children();
    // something involving next_node and list
}
  

Это то, что я в конечном итоге получил, чтобы убедить Rust, что то, что я делал, было в порядке.
То, что я нашел более простым, было:

 let list = self.children();
if list.len() > K {
    let mut next_node = self.next_node();
    // Something involving next_node and list
}
  

Но он жаловался, потому что я не мог получить неизменяемую ссылку на self in next_node , потому что уже была изменяемая ссылка, содержащаяся в self.children , что верно.

В частности, в этой реализации я делаю это только .children() один раз, что в данном случае не является очень сложным методом, но может быть.

Есть ли способ сделать это, который не вычисляет children более одного раза и не создает next_node , когда это не требуется?

Ответ №1:

Простой ответ: нет.


Rust может указывать на непересекающиеся поля при заимствовании, поэтому вы можете изменяемо заимствовать поле, а затем заимствовать другое поле, и это будет работать, если оба они встречаются в одном контексте (функция / метод).

В этом случае то, что next_node делает, непрозрачно для компилятора, и, насколько нам известно, он может фактически использовать дочерние элементы.

Таким образом, требуется, чтобы при вызове next_node не было изменяемого заимствования.


В частности, в этой реализации я делаю это только .children() один раз, что в данном случае не является очень сложным методом, но может быть.

Верно… но какова ответственность этого метода. Зачем ему выполнять как обширные вычисления, так и заимствования?

Более простая организация этого кода будет:

  • выполните дорогостоящее вычисление один раз (не заимствуя)
  • заимствуйте как можно меньшую область

и это сработало бы:

 self.expensive_computation();

if self.children.len() > K {
    let mut next_node = self.next_node();
    let list = self.children;
    // Something involving next_node and list
}