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?
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 {
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`.
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 { ... }
}
}
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);
}
}
}
}
}