Странная ошибка заимствования во время компиляции при использовании `RefCell<T>`

Я использую библиотеку ratatui для создания программы на Rusc 1.77.2.

У меня есть следующий код:

self.terminal().draw(|frame| self.render_frame(frame))?;

где terminal() — это метод получения структуры, содержащей экземпляр терминала, который принимает &self, а draw — это метод ratatui для того, что рисует на экране. self.render_frame берет себя на себя.

если terminal() возвращает &mut Terminal, то это вызывает ошибку заимствования, поэтому вместо terminal() возвращает RefMut<Terminal>, в то время как Self имеет поле terminal, которое содержит RefCell<Terminal, и вызывает self.terminal.borrow_mut() как реализацию terminal().

Моя логика такова: поскольку terminal() возвращает refmut, собственное значение, которое не будет учитываться проверкой заимствований, и пока terminal() не используется в self.render_frame(), проблем не будет.

Однако это не так, поскольку я получаю следующую ошибку:

error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable
  --> src/tui.rs:75:38
   |
75 |                 self.terminal().draw(|frame| self.render_frame(frame))?;
   |                 ---------------      ^^^^^^^ ----                      - ... and the immutable borrow might be used here, when that temporary is dropped and runs the destructor for type `RefMut<'_, ratatui::Terminal<ratatui::backend::CrosstermBackend<Stdout>>>`
   |                 |                    |       |
   |                 |                    |       second borrow occurs due to use of `self` in closure
   |                 |                    mutable borrow occurs here
   |                 immutable borrow occurs here
   |                 a temporary with access to the immutable borrow is created here ...

Что меня сбивает с толку, так это то, почему RefMut учитывается проверкой заимствований, когда соблюдение этих правил во время выполнения якобы и есть весь смысл RefCell.

В частности, мне интересно узнать, почему я использую RefMut/RefCell неправильно и как я могу это исправить; так как я не могу найти никакой информации об ошибках проверки заимствований при использовании Refcell.

Один из способов решить основную проблему — использовать Rc<RefCell<T>>. Затем вы можете clone() Rc полностью отделить время жизни RefCell от времени жизни объекта, на который он указывает. Rc<RefCell<_>> считается своего рода антипаттерном в Rust, но это полезный инструмент, о котором следует знать, если он вам понадобится.

user4815162342 03.05.2024 08:32
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
1
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Что меня сбивает с толку, так это то, почему RefMut учитывается проверкой заимствований, когда соблюдение этих правил во время выполнения якобы и есть весь смысл RefCell.

Обеспечение соблюдения правил во время выполнения — это суть RefCell, но в отношении его содержания. Единственное, что дают вам такие типы внутренней изменчивости, как RefCell, — это возможность мутировать через общую ссылку. В случае RefCell это позволяет вам получить &mut T из &RefCell<T>. Другими словами, это позволяет вам «обновить» общий заимствование (&) до эксклюзивного заимствования (&mut), но для этого вам все равно понадобится общий заимствование RefCell.

Это отражено в подписи RefCell::borrow_mut:

pub fn borrow_mut(&self) -> RefMut<'_, T>

Если мы обесценим явно исключенное время жизни:

pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T>

Итак, вы можете видеть, что RefMut заимствовано из *self (RefCell). (Если это не так, это означает, что вы можете отбросить RefCell, пока у вас есть RefMut, указывающий на него. Это было бы неправильно!)

Похоже, что render_frame занимает &mut self, что и вызывает проблему: RefMut удерживает общий заимствование против self, но render_frame хочет получить эксклюзивный заимствование.

Трудно точно сказать, как решить эту проблему, не видя остальную часть вашего кода. Один из вариантов — использовать render_frame вместо &self, что в данном случае разрешено. Однако для этого может потребоваться, чтобы больше частей этого типа использовали внутреннюю изменчивость.

Очень похоже на то, что вы подходите к этой проблеме с объектно-ориентированной точки зрения, и такой дизайн, как правило, не очень хорошо работает в Rust. Возможно, какой бы тип &mut self здесь ни представлял, он делает слишком много, и его нужно разделить.

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

Почему это неизменяемое поле ссылки содержит изменяемый заимствование
Как инициализировать две переменные в операторе сопоставления, не вызывая раздражения средства проверки заимствований?
Перенос кода C с изменяемыми заимствованиями — ошибка во время выполнения (пример nappgui)
Не могу заимствовать *self как неизменяемый, но не могу найти обходной путь
Возвращает принадлежащее значение и ссылку на значение
Заимствовать некоторое изменяемое значение дважды, если известно, что изменяемое значение является неизменяемым
Почему мой параметр типа в этом блоке impl не ограничен?
Какую структуру можно создать, чтобы избежать использования RefCell?
Почему средство проверки заимствований в Rust жалуется при использовании итератора, возвращаемого из метода, но не при непосредственном использовании итератора Vec?
Как лучше всего распараллелить код, изменяя несколько фрагментов одного и того же вектора Rust?