Разница между заимствованием_мута на RefCell ‹X› и RefCell ‹& X›

Если я понял, невозможно создать изменяемое заимствование поверх std::rc::Rc в Rust, вы должны использовать Cell или RefCell. Но все равно я не могу понять, как ими пользоваться. Например, рассмотрим этот простой пример:

use std::cell::RefCell;

struct X (i32);

impl X {
    fn foo(&mut self) {
        self.0 = 0;
    }
}

fn main () {
    let x = X(5);
    let rcx = RefCell::new(&x);

    let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();
    (*mutx).foo();
}

Я получаю следующую ошибку:

16:5: 16:9 error: cannot borrow immutable local variable `mutx` as mutable
16     mutx.foo();

Но если я удалю ссылку из строки (и обновлю тип mutx):

let rcx = RefCell::new(x);

Все отлично. Но я не могу понять почему, поскольку RefMut::deref_mut() -> &mut T почтение, вызванное в строке 16, должно возвращать &&mut T в первом случае, а &mut T во втором случае. Но поскольку компилятор должен применять много * по мере необходимости (если я понимаю, как deref coercion работает) не должно быть разницы между RefMut<X>::deref_mut() и RefMut<&X>::deref_mut()

Изменить: по ошибке я забыл написать mut в строке 15, так как в связанном примере это правильно написано. Итак, теперь let mut mutx...


person Kill KRT    schedule 15.09.2015    source источник


Ответы (1)


Проблема связана с тем, что вы сохранили неизменяемую ссылку в RefCell. Я не понимаю, зачем вам это нужно. Обычный шаблон - поместить в RefCell все значение, а не только ссылку:

fn main () {
    let rcx = RefCell::new(X(5));

    let mut mutx = rcx.borrow_mut();
    mutx.foo();
}

Проблема с исходным вопросом

У вас есть две ошибки сложения. Давайте проверим все сообщение об ошибке:

<anon>:16:5: 16:12 error: cannot borrow immutable borrowed content as mutable
<anon>:16     (*mutx).foo();
              ^~~~~~~
<anon>:16:7: 16:11 error: cannot borrow immutable local variable `mutx` as mutable
<anon>:16     (*mutx).foo();
                ^~~~

Обратите внимание на вторую ошибку - «невозможно заимствовать неизменяемую локальную переменную mutx». Это потому, что вам нужно объявить переменную mutx изменчивой:

let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();

Это позволит mutx участвовать в DerefMut.

person Shepmaster    schedule 15.09.2015
comment
Извините, я поместил mut в связанный код и не обновил код в сообщении. В любом случае (относительно первой проблемы), если я использую RefCell::new(x) (без ссылки), я сохраняю неизменное значение в RefCell, но это не вызывает никаких ошибок. Причина, по которой я использую здесь ссылку, заключается в том, что мой исходный код на самом деле немного сложнее (вы можете проверить здесь: is .gd / XoROad) - person Kill KRT; 15.09.2015
comment
RefCell::new(x) [хранит] неизменное значение - это распространенное заблуждение, но это не неизменное значение. У вас может быть неизменяемая привязка к значению, но когда вы передаете право собственности, вы можете выбрать изменяемость у вас есть. - person Shepmaster; 15.09.2015
comment
@KillKRT Хотя Шепмастер прав, это могло бы быть более ясным. Что вам нужно понять, так это то, что значения имеют фиксированное местоположение (в стеке, куче и т. Д.). Они могут быть неизменными. Однако при переходе от одного значения к другому foo = bar право собственности на содержащуюся память передается. Эта передача может происходить в изменяемое место, которое затем может предоставлять транзитивный изменяемый доступ к содержимому. Но вы не можете сделать это со ссылками, потому что вы не можете выйти из ссылок, поэтому вы никогда не сможете переместить данные, на которые есть ссылки, в изменяемое место. - person Veedrac; 16.09.2015
comment
@KillKRT RefCell также немного особенный, потому что он позволяет нарушить транзитивность изменчивости: неизменяемый RefCell может предоставить вам изменяемый доступ к своему содержимому. Если его содержимое является значением, оно может дать &mut T. Однако, если его содержимое является ссылкой, оно может дать вам только &mut &T, что позволяет вам изменить место, на которое указывает внутренняя ссылка, но не значение, на которое указывает внутренняя ссылка. - person Veedrac; 16.09.2015
comment
@Veedrac, спасибо за разъяснения, они мне очень помогли. - person Kill KRT; 16.09.2015