💭 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 提供的终端对话应用例子:
例子来源:
源讨论:
stackoverflow: how-do-i-print-stdout-and-get-stdin-on-the-same-line-in-rust