回顾一下猜数字游戏:产生 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 usedlet _ = 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 的 inputlet input: String = Input::new().with_prompt("Your name").interact_text().unwrap();println!("Hello {}!", input);// 终端带样式的 inputlet input: String = Input::with_theme(&ColorfulTheme::default()).with_prompt("Your name").interact_text().unwrap();println!("Hello {}!", input);}

这个 crate 提供的终端对话应用例子:
例子来源:
源讨论:
stackoverflow: how-do-i-print-stdout-and-get-stdin-on-the-same-line-in-rust
