从 gussing game 的联想:print! 与终端输入

💭 gussing game

回顾一下猜数字游戏:产生 0-100 随机整数,由用户输入数字,程序会提示输入值相比于随机数的大小,从而使用户猜对。

use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("Guess the number!");

    // 注意 gen_range(1..101) 而不是 gen_range(1, 101)
    let secret_number = rand::thread_rng().gen_range(1..101);

    println!("The secret number is: {}", secret_number);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        // let guess: u32 = guess.trim().parse().expect("Please type a number!");
        let guess: u32 = match guess.trim().parse() {
            Ok(x) => x,
            Err(_) => {
                print!("Not a NUMBER. ");
                continue;
            }
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

🤔 print! 和 stdin 不在同一行显示

应用此例子时我产生一个疑问:

println!("Please input your guess: "); 和 输入(stdin)并不在同一行。这很好理解,因为 println! 显然多了一个换行。

如果需要打印输出和捕获输入在一行显示,是不是换成 print! 就行了呢?结果很可能是这样:

输入的内容
Please input your guess: 

而不是我们期待的:

Please input your guess: 输入的内容

在 python3 中,我们可以直接使用 variable = input("Please input your guess: ") 来获取终端输入,并且输出与输入显示在同一行。
然而在 Rust 中,即便 print! 先于 stdin 出现,终端显示并不是这个顺序。
原因在于终端输出(stdout)在新的行(换行)之后才刷新出现
由于 print! 语句既不包含换行,又不以换行结尾,所以无法触发屏幕刷新和显示。
stdin().read_line() 有一个换行符 \n,刷新了屏幕,所以导致意料之外的情况。
我们可以用 println!({:?}, ) 验证一下捕获输入的换行符:

use std::io;
fn main() {
    print!("Input: ");

    let mut t = String::new();

    io::stdin().read_line(&mut t).expect("Failed to read line.");

    println!("{:?}", t); 
}

💡 解决方式1:flush 手动刷新

既然 print! 不触发刷新,那么自己手动刷新一下就能解决不在同一行显示的问题,代码是 std::io::stdout().flush(),不过 flush 方法还需调用 std::io::Write trait,最终的代码:

use std::io::{self, Write};  
fn main() {
    print!("Input: ");
    // 如果没有把 flush 的结果赋值,编译器会提示 unused `Result` that must be used
    let _ = std::io::stdout().flush(); 

    let mut t = String::new();   
    io::stdin().read_line(&mut t).expect("Failed to read line");  
  
    println!("{:?}", t);  
}
Input: 1
"1\n"

💡💡 解决方法2:dialoguer 终端对话利器

达到 py 的 input 类似的效果,甚至更 fancy 的操作:使用 dialoguer crate,参考 dialoguer/examples/input.rs,比如以下例子:

use dialoguer::{theme::ColorfulTheme, Input};

fn main() {
    // 类似 py3 的 input
    let input: String = Input::new()
        .with_prompt("Your name")
        .interact_text()
        .unwrap();
    println!("Hello {}!", input);

    // 终端带样式的 input
    let input: String = Input::with_theme(&ColorfulTheme::default())
        .with_prompt("Your name")
        .interact_text()
        .unwrap();
    println!("Hello {}!", input);
}

在这里插入图片描述

这个 crate 提供的终端对话应用例子:
在这里插入图片描述


例子来源:

  1. Rust Book: guessing-game-tutorial
  2. Rust 程序设计语言 简体中文版:编写 猜猜看 游戏

源讨论:
stackoverflow: how-do-i-print-stdout-and-get-stdin-on-the-same-line-in-rust

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值