Help using Termion's AsyncReader i.e., ownership issue

Hello, Rust noob here!

I want to learn Rust by writing a simple game for the terminal. I'm trying to figure out how to read input events from the user with the Termion library's AsyncReader. Here's where I'm at.

fn main() {
    {
        let mut exit = false;
        let user_input = termion::async_stdin();
        // ... other initialization redacted...

        while exit == false {

           // ...other main loop stuff redacted...

            let input_events = user_input.events();  // <--- error here
            for event in input_events {
                match event {
                    Ok(event) => match event {
                        Event::Key(_) => {
                            exit = true;
                        }
                        Event::Mouse(_) => {}
                        Event::Unsupported(_) => {}
                    },
                    Err(error) => {
                        panic!("Error when receiving input events: {}", error);
                    }
                }
            }
        }
    }
}

When compiling, I get

error[E0382]: use of moved value: `user_input`
   --> src/main.rs:39:32
    |
14  |         let user_input = termion::async_stdin();
    |             ---------- move occurs because `user_input` has type `AsyncReader`, which does not implement the `Copy` trait
...
39  |             let input_events = user_input.events();
    |                                ^^^^^^^^^^ -------- `user_input` moved due to this method call, in previous iteration of loop
    |
note: this function takes ownership of the receiver `self`, which moves `user_input`
   --> /home/r/.cargo/registry/src/github.com-1ecc6299db9ec823/termion-1.5.6/src/input.rs:108:15
    |
108 |     fn events(self) -> Events<Self> where Self: Sized;
    |               ^^^^

For more information about this error, try `rustc --explain E0382`.
error: could not compile `gotcha` due to previous error

I'm not yet fluent enough on ownershop topics to figure this out on my own. Can anyone point me in the right direction?

Thanks,
Richard

Does moving the creation of the events() iterator to outside the loop work for your use case?

         let user_input = termion::async_stdin();
+        let input_events = user_input.events();
 
         while exit == false {
-            let input_events = user_input.events();  // <--- error here
-            for event in input_events {
+            for event in &mut input_events {
                 match event {

(Untested.)

No. When I do that I get

rror[E0382]: use of moved value: `input_events`
   --> src/main.rs:41:26
    |
16  |         let input_events = user_input.events();
    |             ------------ move occurs because `input_events` has type `Events<AsyncReader>`, which does not implement the `Copy` trait
...
41  |             for event in input_events {
    |                          ^^^^^^^^^^^^
    |                          |
    |                          `input_events` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
    |                          help: consider borrowing to avoid moving into the for loop: `&input_events`
    |
note: this function takes ownership of the receiver `self`, which moves `input_events`
   --> /home/rellwood/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:234:18
    |
234 |     fn into_iter(self) -> Self::IntoIter;
    |                  ^^^^

For more information about this error, try `rustc --explain E0382`.

I tried the compiler's suggestion and put

            for event in &input_events {

But then I get

error[E0277]: `&Events<AsyncReader>` is not an iterator
  --> src/main.rs:41:26
   |
41 |             for event in &input_events {
   |                          -^^^^^^^^^^^^
   |                          |
   |                          `&Events<AsyncReader>` is not an iterator
   |                          help: consider removing the leading `&`-reference
   |
   = help: the trait `Iterator` is not implemented for `&Events<AsyncReader>`
   = note: `Iterator` is implemented for `&mut termion::input::Events<termion::AsyncReader>`, but not for `&termion::input::Events<termion::AsyncReader>`
   = note: required because of the requirements on the impl of `IntoIterator` for `&Events<AsyncReader>`

For more information about this error, try `rustc --explain E0277`.

Any thoughts? Thanks.

How about &mut instead of &? (Iterating is a mutable operation.)

Silly question, but why do you need a while exit == false loop?

Can't you just do something like this:

let user_input = termion::async_stdin();

for event in user_input.events() {
  match event {
    Ok(Event::Key(_)) => break,   // Note: use break instead of an exit flag
    Ok(Event::Mouse(_)) => ...,
    ...
    Err(error) => ...,
  }
}

Termion should keep giving you events until the process is killed or the user closes stdin.

Otherwise if it is necessary, you can hoist the events iterator out of the loop and use a while let that keeps grabbing the next event until none are left.

let mut exit = false;

let user_input = termion::async_stdin();
let mut events = user_input.event();

while !exit {
  ...

  while let Some(event) = events.next() {
    match event { ... }
  }
}
1 Like

Yes, that did it, thank you. In fact I see you had that in your initial reply and I missed it.

1 Like

I tried that and found that the program exits immediately. It seems that events() returns an empty iterator if there are no events waiting. So, following @quinedot's suggestion I now have something like

fn main() {
    // ...
    let mut input_events = termion::async_stdin().events();

    while quit == false {
        // ...
        for input_event in &mut input_events {
            match input_event {
                Ok(event) => match event {
                    Event::Key(key) => match key {
                        _ => {
                            quit_requested = true;
                        }
                    },
                    Event::Mouse(_) => {
                        quit_requested = true;
                    },
                    _ => {},
                },
                Err(error) => {
                    panic!("Error when receiving input events: {}", error);
                }
            }
        }
    }
}

Thank you very much! Case closed.

1 Like

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.