在开始学习 Rust 语言后,我一直在用 Rust 编写一个简单的基于文本的游戏,并且我尝试实现一个函数来等待用户按 Enter 键,然后再继续程序。
到目前为止,经过一些实验我已经做到了:
pub fn wait() {
let mut stdin = io::stdin();
let wait_string = &mut String::new();
wait_string.clear();
println!("\nPress Enter to Continue\n");
io::stdout().clear();
stdin.read_line(wait_string);
}
Run Code Online (Sandbox Code Playgroud)
然而,我的问题是,由于标准输入上的硬编码缓冲区,如果用户在显示消息之前按下 Enter 键,该函数将自动继续,而无需等待。
有针对这个的解决方法吗?或者使用 std::thread::sleep 的另一种方法会更好吗?如果这很容易解决或不可能解决,我深表歉意,我仍在 Rust 中找到立足点。
您可以临时切换到底层文件描述符上的非阻塞操作,以便消耗在此wait()调用之前可能输入的所有内容,然后切换回阻塞操作以实际等待输入。
这个例子需要libccrate并且只能在 Unix 上运行(我想使用winapicrate在 Windows 上也可以完成类似的事情)。
pub fn change_blocking_fd(
fd: std::os::unix::io::RawFd,
blocking: bool,
) {
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFL);
libc::fcntl(
fd,
libc::F_SETFL,
if blocking {
flags & !libc::O_NONBLOCK
} else {
flags | libc::O_NONBLOCK
},
);
}
}
pub fn wait() {
use std::os::unix::io::AsRawFd;
let stdin = std::io::stdin();
change_blocking_fd(stdin.as_raw_fd(), false);
let mut wait_string = String::new();
while stdin.read_line(&mut wait_string).is_ok_and(|n_bytes| n_bytes > 0) {
println!("discard: {:?}", wait_string); // debug purpose only
wait_string.clear();
}
change_blocking_fd(stdin.as_raw_fd(), true);
println!("\nPress Enter to Continue\n");
stdin.read_line(&mut wait_string).expect("!!!");
}
fn main() {
println!("enter a few lines during five seconds");
std::thread::sleep(std::time::Duration::from_millis(5000));
println!("~~~~ before wait() ~~~~");
wait();
println!("~~~~ after wait() ~~~~");
}
/*
enter a few lines during five seconds
aaa
zzz
eee
~~~~ before wait() ~~~~
discard: "aaa\n"
discard: "zzz\n"
discard: "eee\n"
Press Enter to Continue
~~~~ after wait() ~~~~
*/
Run Code Online (Sandbox Code Playgroud)