How to fix "cannot borrow `*self` as mutable because it is also borrowed as immutable"

Hi folks !

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.

But trait do not allow me to define properties and when I trying to use self.items or self.state directly in trait method:

let list = List::new(self.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.state);

I got an error:

error[E0609]: no field `state` on type `&mut Self`
  --> src/primary/traits/ui_component.rs:62:60
   |
14 | pub trait UIModalComponent {
   | -------------------------- type parameter 'Self' declared here
...
62 |         frame.render_stateful_widget(list, rect, &mut self.state);
   |                                                            ^^^^^

Add a method to the trait that gives you both at the same time:

fn get_items_and_state(&mut self) -> (Vec<ListItem>, &mut ListState);

Then use this in render.

1 Like

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`

Let me share the component example:

pub struct RealmModal {
    state: ListState,
    realms: Vec<Realm>,
    _sender: Sender<HandlerOutput>,
}

impl UIModalComponent for RealmModal {
    fn new(options: UIComponentOptions) -> Self {
        Self {
            state: ListState::default(),
            realms: vec![],
            _sender: options.sender,
        }
    }

    fn get_title() -> String {
        "SELECT REALM".to_string()
    }

    fn get_items(&self) -> Vec<ListItem> {
        self.realms
            .iter()
            .map(|realm| ListItem::new(vec![
                Spans::from(vec![
                    Span::raw("Connect to ["),
                    Span::styled(
                        realm.name.to_string(),
                        Style::default()
                            .fg(Color::LightGreen)
                            .add_modifier(Modifier::BOLD)
                    ),
                    Span::raw("]: "),
                    Span::styled(
                        realm.address.to_string(),
                        Style::default()
                            .fg(Color::LightYellow)
                    ),
                ])
            ]))
            .collect()
    }

    fn get_state(&mut self) -> &mut ListState {
        &mut self.state
    }

    fn get_items_and_state(&mut self) -> (Vec<ListItem>, &mut ListState) {
        let items = self.get_items();
        (items, &mut self.state)
    }
}

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".

2 Likes

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 !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.