如何从 stdin 读取非阻塞?

How can I read non-blocking from stdin?

有没有办法在 Rust 中检查 stdin 上的数据是否可用,或者使用当前可用数据立即读取 returns?

我的目标是能够读取 shell 中光标键产生的输入,该 shell 设置为 return 立即读取所有数据。例如等效于:stty -echo -echok -icanon min 1 time 0.

我想一个解决方案是使用 ncurses 或类似的库,但我想避免任何类型的大依赖。

到目前为止,我只得到阻塞输入,这不是我想要的:

let mut reader = stdin();
let mut s = String::new();

match reader.read_to_string(&mut s) {...} // this blocks :(

大多数操作系统默认以阻塞方式使用标准输入和输出。难怪 Rust 库会随之而来。

要以非阻塞方式从阻塞流中读取数据,您可以创建一个单独的线程,这样额外的线程就会阻塞而不是主线程。检查阻塞文件描述符是否产生了一些输入是类似的:产生一个线程,让它读取数据,检查它到目前为止是否产生了任何数据。

Here's a piece of code that I use with a similar goal of processing a pipe output interactively and that can hopefully serve as an example. It sends the data over a channel, which supports the try_recv 方法 - 允许您检查数据是否可用。

Someone has told me that mio might be used to read from a pipe in a non-blocking way, so you might want to check it out too. I suspect that passing the stdin file descriptor (0) to PipeReader::from_fd 应该可以。

您也可以考虑使用 ncurses (also on crates.io),这样您就可以在原始模式下阅读。 Github 存储库中有几个示例展示了如何执行此操作。

正在将 OP 的评论转换为答案:

您可以生成线程并通过 channel. You can then poll that channel in the main thread using try_recv.

发送数据
use std::io;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::TryRecvError;
use std::{thread, time};

fn main() {
    let stdin_channel = spawn_stdin_channel();
    loop {
        match stdin_channel.try_recv() {
            Ok(key) => println!("Received: {}", key),
            Err(TryRecvError::Empty) => println!("Channel empty"),
            Err(TryRecvError::Disconnected) => panic!("Channel disconnected"),
        }
        sleep(1000);
    }
}

fn spawn_stdin_channel() -> Receiver<String> {
    let (tx, rx) = mpsc::channel::<String>();
    thread::spawn(move || loop {
        let mut buffer = String::new();
        io::stdin().read_line(&mut buffer).unwrap();
        tx.send(buffer).unwrap();
    });
    rx
}

fn sleep(millis: u64) {
    let duration = time::Duration::from_millis(millis);
    thread::sleep(duration);
}