首先我们看如下代码:
use std::io::{self, Read};
fn main() {
for b in io::stdin().bytes() {
let c = b.unwrap() as char;
println!("{}", c);
}
}
尝试运行运行一下代码,我们可以发现,这段代码会显示我们在terminal中输入的内容:例如:

这里面有很多细节,例如,不会立即输出我们从键盘的输入,只会在我们输入内容后,按下回车键,才会输出我们输入的内容。
当我们输入Ctrl-C(Ctrl-D)才会退出(终止)整个程序。
这种行为对许多程序都很有用:它允许用户输入一行文本,然后Backspace修改错误,直到输入完全符合他们的要求,最后按下Enter将其发送到程序。这就是终端的工作方式,与 Rust 无关。但对于用户界面更复杂的程序(例如文本编辑器),这种方法效果不佳。我们希望在每次按键时都进行处理,以便能够立即响应。
<aside> 📓
在 Unix 终端下,“原始模式”(raw mode)和“熟模式”(cooked mode,又称 canonical mode)是由终端驱动(tty driver)来控制的两种输入处理方式:
熟模式(canonical mode)
- 输入以行(line)为单位缓冲,只有按下回车后,输入才会一次性提交给应用程序。
- 终端会负责本地编辑(如退格删除)、回显(echo)字符,以及处理特殊字符(如 Ctrl‑C、Ctrl‑Z)等。
原始模式(raw mode,又称非 canonical mode) Unix & Linux Stack Exchange
- 关闭行缓冲,按下的每个按键都会即刻(而不是等到回车)交给应用程序。
- 关闭字符回显,应用程序自行决定是否回显;同时,特殊字符(中断信号、流控制等)也不会被终端拦截和处理,而是直接传给程序。 </aside>
作为编辑器我们需要的是raw模式,在Rust中我们可以通过crossterm这个crate进入raw模式,首先添加项目依赖
cargo add crossterm
添加完毕后我们下面我们看看该如何使用crossterm,代码如下(绿色部分为新添加的代码,项目源码见参考文章):

在代码的第5行我们进入raw模式,在输入‘q’后第10行退出raw模式,进入熟模式。
尝试一下,你会发现你输入的每个字符都会立即打印出来,而且一旦你输入q,程序就会结束。
当前我们是使用crossterm这个crate的enable_raw_mode()函数直接进入raw模式的,这种方式很简洁,当然我们也可以自己编写rust的相关代码进入raw模式,但是这种方式比较复杂,我们看下enable_raw_mode() 的实现大概就知道了,或者看一下kilo的原始C语言实现。
为了更好地理解原始模式下输入的工作原理,让我们打印出读取到的每个字节。我们将打印每个字符的二进制表示、其 ASCII 值,以及如果它是可打印字符,则打印它所代表的字符。我们通过main()如下更改来实现这一点:
