Я использую библиотеку 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.

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