I have a trait to apply for UI component created with tui-rs:
pub trait UIModalComponent {
fn new(options: UIComponentOptions) -> Self where Self: Sized;
fn get_title() -> String;
fn get_items(&self) -> Vec<ListItem>;
fn get_state(&mut self) -> &mut ListState;
fn prev(&mut self) {
let total_items = { self.get_items().len() };
let state = self.get_state();
let i = match state.selected() {
Some(i) => if i == 0 { total_items - 1 } else { i - 1 },
None => 0,
};
state.select(Some(i));
}
fn next(&mut self) {
let total_items = { self.get_items().len() };
let state = self.get_state();
let i = match state.selected() {
Some(i) => if i >= total_items - 1 { 0 } else { i + 1 },
None => 0,
};
state.select(Some(i));
}
fn render<B: Backend>(&mut self, frame: &mut Frame<B>, rect: Rect) {
let block = Block::default()
.title(Self::get_title())
.title_alignment(Alignment::Center)
.style(Style::default().bg(Color::Black))
.borders(Borders::ALL)
.border_type(BorderType::Plain);
let list = List::new(self.get_items())
.block(block)
.style(Style::default().fg(Color::White))
.highlight_style(Style::default().add_modifier(Modifier::ITALIC))
.highlight_symbol(">> ");
frame.render_widget(Clear, rect);
frame.render_stateful_widget(list, rect, &mut self.get_state());
}
}
And my render method has an error on compile:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/primary/traits/ui_component.rs:62:55
|
55 | let list = List::new(self.get_items())
| ---- immutable borrow occurs here
...
62 | frame.render_stateful_widget(list, rect, &mut self.get_state());
| ---------------------- ^^^^^^^^^^^^^^^^ mutable borrow occurs here
| |
| immutable borrow later used by call
Could somebody explain what could be the best way to fix this issue ?
You can't use getters. The code may compile if you use fields directly, like &self.items and &mut self.state.
This is because getters have &self or &mut self arguments, and that really means borrowing all of self, with all fields, and you're not allowed to borrow &mut self and separately anything else from self at the same time.
When you use fields directly, the borrow checker borrows the individual fields, so their loans don't overlap.
Unfortunately, this not helped. I still have an error:
error[E0502]: cannot borrow `self.state` as mutable because it is also borrowed as immutable
--> src/features/ui/realm_modal.rs:102:17
|
100 | fn get_items_and_state(&mut self) -> (Vec<ListItem>, &mut ListState) {
| - let's call the lifetime of this reference `'1`
101 | let items = self.get_items();
| ---- immutable borrow occurs here
102 | (items, &mut self.state)
| --------^^^^^^^^^^^^^^^-
| | |
| | mutable borrow occurs here
| returning this value requires that `*self` is borrowed for `'1`
First, I'd recommend you use #![deny(elided_lifetimes_in_paths)], which willmake the issue clearer. get_items borrows from self as long as its return value is used, because ListItem has a lifetime parameter. With your implementation, you just moved code around. One possible solution is sadly to copy the implementation of get_items into get_items_and_state. The keyword for further research is "partial borrowing".
Oh, it was my fall. Your idea with get_items_and_state works perfectly. My fall was that I didn't remove the get_items and get_state methods from the code. Thank you very much !