Home > Mobile >  Read_line() don´t wait for users input in loop. Rust
Read_line() don´t wait for users input in loop. Rust

Time:01-22

What program shoud do?

  1. Read two numbers separated by space from terminal e.g. "10 8".
  2. Split the string to Vec<&str>.
  3. Parse Vec[0] and Vec[1] to num1 and num2.
  4. Read (math) operation from terminal and parse it.
  5. Print the "math" and result. (10 * 80 = 80)
  6. Catch almost every error in steps 2. - 4. and run try_again function.
  7. If all goes right than run try_again function with "Success" msg

(the main function is there only for context, the problem is in try_again function)

use std::io::{self, Write, Read, Error, ErrorKind};

const ERR_MSG_STDIN_READ: &str = "Problem with getting input";
const ERR_MSG_STDOUT_FLUSH: &str = "Problem with flushing stdout";

fn main() {
    loop {
        clear_terminal();

        print!("Write 2 ints separated by space: ");
        let mut nums_string = String::new();
        io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
        io::stdin().read_line(&mut nums_string).expect(ERR_MSG_STDIN_READ);
        let nums_str_vec: Vec<&str> = nums_string.trim()
.split(' ').collect();
        let num1: i32 = match nums_str_vec[0].parse() {
            Ok(num) => num,
            Err(_) => match try_again("Error") {
                true => continue,
                false => break
            }
        };
        let num2: i32 = match nums_str_vec[1].parse() {
            Ok(num) => num,
            Err(_) => match try_again("Error") {
                true => continue,
                false => break
            }
        };

        print!("Write one of these[ ,-,*,/] maths operation for 2 inputed ints: ");
        let mut operation_string = String::new();
        io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
        io::stdin().read_line(&mut operation_string).expect(ERR_MSG_STDIN_READ);
        let operation = match operation_string.trim().parse() {
            Ok(char) => char,
            Err(_) => match try_again("Error") {
                true => continue,
                false => break
            }
        };
        match operation {
            ' ' => println!("{} {} {} = {}", num1, operation, num2, num1   num2),
            '-' => println!("{} {} {} = {}", num1, operation, num2, num1 - num2),
            '*' => println!("{} {} {} = {}", num1, operation, num2, num1 * num2),
            '/' => println!("{} {} {} = {} ({})", num1, operation, num2, num1 / num2, num1 % num2),
            _ => match try_again("Error") {
                true => continue,
                false => break
            }
        }
        io::stdin().read(&mut [0]).expect(ERR_MSG_STDIN_READ);
        match try_again("Success") {
            true => continue,
            false => break
        }
    }
}

fn clear_terminal() {
    print!("{}c", 27 as char);
    io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
}

What try_again function shoud do?

  1. Save msg to msg_new for later changing.
  2. Read (y/n) answer from terminal and parse it.
  3. If 'y' return true if 'n' return false.
  4. Catch almost every error in step 2. and continue "self loop" again. (With "Error" msg)
  5. The program (in main function) match returned result of try_again function => true means continue main loop, false means break the loop and end the program.
fn next(msg: &str) -> bool {
    let mut msg_new = msg;
    loop {
        println!("The calculation end up with {}.", msg_new);
        print!("Do you want to make antoher calculation? (y/n): ");
        let mut next_string = String::new();
        stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
        stdin().read_line(&mut next_string).expect(ERR_MSG_STDIN_READ);
        match next_string.trim().parse() {
            Ok('y') => true,
            Ok('n') => false,
            _ => {
                msg_new = "Error";
                continue
            }
        };   
    }
}

Whats the problem?

What doesn´t work in program?

step 7 (maybe we can say 6 and 7) the problem is in try_again function.

What doesn´t work in try_again function

When the program run try_again("Success");:

  1. In the first loop stdin().read_line(&mut next_string) doesn´t wait for users input and immediately go to _ => arm.
  2. And continue "self loop" again (with "Error" msg)
  3. In the second loop actually wait for input and work but with wrong msg.

Final question

Why the stdin().read_line(&mut next_string) wait for input in secodn round of loop? How to solve it? Thanks for every answer!


I am totally new to rust, please explain everything what you write. OS: Windows 10, terminal: cmd (Command Promt)

CodePudding user response:

You must check whether a 'y' or a 'n' was entered at all, e.g. with a guard:

fn next(msg: &str) -> bool {
    let msg_new = msg;
    loop {
        clear_terminal();
    
        println!("The calculation end up with {}.", msg_new);
        print!("Do you want to make antoher calculation? (y/n): ");
        let mut next_string = String::new();
        stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
        match std::io::stdin().read_line(&mut next_string) {
            Ok(n) if n > 0 && next_string.chars().next().unwrap() == 'y' => break true,
            Ok(n) if n > 0 && next_string.chars().next().unwrap() == 'n' => break false,
            Ok(_) => {},  // other input is ignored
            Err(err) => panic_any(err),
        };
    }
}

Btw: next should be reserved for iterators

CodePudding user response:

When you enter y or n, the program does not go to the match arm with continue.

It sets the return value of the match statement to either true or false (which is never stored in a variable though) and then goes to the next iteration of the loop.

I guess you would actually like to return the boolean values from the next function in these match arms as shown below:

fn next(msg: &str) -> bool {
    let mut msg_new = msg;
    loop {
        clear_terminal();
    
        println!("The calculation end up with {}.", msg_new);
        print!("Do you want to make antoher calculation? (y/n): ");
        let mut next_string = String::new();
        stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
        stdin().read_line(&mut next_string).expect(ERR_MSG_STDIN_READ);
        match next_string.trim().parse() {
            Ok('y') => return true,
            Ok('n') => return false,
            _ => {
                msg_new = "Error";
                continue
            }
        };   
    }
}
  • Related