'use an object inside a closure which is passed to a method of that object

i have a struct Screen with its implementation

pub struct Screen {
    stdin:  Stdin,
// types are irrelevant
    stdout: MouseStdout,
}

impl Screen {
   // ...
    pub fn handle_keys_loop<F: FnMut(&Event) -> ()>(
        &self,
        mut handler: F,
    ) {
        let stdin = stdin();
        for e in stdin.events() {
            let e = e.unwrap();
            match e {
                Event::Key(Key::Ctrl('c')) => break,
                _ => {
                    handler(&e);
                },
            }

        }
    }
}

and usage (which is wrong and know it)

    let mut screen = Screen::new();
    screen.init_screen();

    screen.handle_keys_loop(|event| {
        match event {
            Event::Key(Key::Char('a')) => {
                screen.println("hello there",15, 1, true);
            },
            _ => {}
        }
    });

    screen.end_screen();

the error is

error[E0502]: cannot borrow `screen` as mutable because it is also borrowed as immutable
  --> src/bin/terminal.rs:74:29
   |
74 |       screen.handle_keys_loop(|event| {
   |       -      ---------------- ^^^^^^^ mutable borrow occurs here
   |       |      |
   |  _____|      immutable borrow later used by call
   | |
75 | |         match event {
76 | |             Event::Key(Key::Char('a')) => {
77 | |                 println!("{} {} {} {}", "do something with a", 15, 1, true);
78 | |                 // tried to borrow as mutable
79 | |                 screen.println("hello there",15, 1, true);
   | |                 ------ second borrow occurs due to use of `screen` in closure
...  |
82 | |         }
83 | |     });
   | |______- immutable borrow occurs here

and if i make self mut inside handle_keys_loop to get rid of cannot borrow screen as mutable because it is also borrowed as immutable

    pub fn handle_keys_loop<F: FnMut(&Event) -> ()>(
        + &mut self,
        - &self
....

i get this error

error[E0499]: cannot borrow `screen` as mutable more than once at a time
  --> src/bin/terminal.rs:74:29
   |
74 |       screen.handle_keys_loop(|event| {
   |       -      ---------------- ^^^^^^^ second mutable borrow occurs here
   |       |      |
   |  _____|      first borrow later used by call
   | |
75 | |         match event {
76 | |             Event::Key(Key::Char('a')) => {
77 | |                 screen.println("hello there",15, 1, true);
   | |                 ------ second borrow occurs due to use of `screen` in closure
...  |
80 | |         }
81 | |     });
   | |______- first mutable borrow occurs here

what im trying to do: use the method handle_keys_loop of screen and pass screen inside the closure, which is passed to handle_keys_loop. basically, to use screen inside of screen.

how do i achieve that ?

some people told me to use RefCell, but that didnt work out very well, i got BorrowError.

i will use any workaround to just use screen inside the closure which is passed to screen's method.

thanks in advance.



Solution 1:[1]

One pattern I use to handle such a situation is to pass self: &mut Self back into the closure from handle and use that inside the closure. A simplified version of your code:

struct Screen {}

struct Event {}

impl Screen {
    fn handle<F: FnMut(&mut Screen, Event)>(&mut self, mut handler: F) {
        handler(self, Event {})
    }

    fn print(&mut self) {}
}

fn main() {
    let mut screen = Screen {};
    screen.handle(|screen, _event| screen.print());
    screen.handle(|screen, _event| screen.print());
}

Solution 2:[2]

You can't do what you're trying to do because it violates Rust's rule that you can't mutate a value while something else has access to it.

However, your handle_keys_loop method doesn't even use self which means the &self parameter is redundant. There's no reason to give a function an argument it's not going to use (except when implementing a trait that requires it).

Just remove the argument:

    pub fn handle_keys_loop<F: FnMut(&Event) -> ()>(
        mut handler: F,
    ) {

And call it as Screen::handle_keys_loop(|event| { ... }).

Alternatively, make it a free function, external to Screen entirely, since it doesn't depend on Screen in any way.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dogbert
Solution 2 cdhowie