2 个版本
0.1.1 | 2019年9月23日 |
---|---|
0.1.0 | 2019年9月20日 |
#323 in 命令行界面
2,430 每月下载
用于 16 个crate(12个直接使用)
9KB
平静I/O
在Rust中向标准输出和错误流写入很容易:使用其中一个 print!
、println!
(stdout)、eprint!
或 eprintln!
(stderr) 宏来格式化消息并将其直接打印到适当的文件描述符。
但是,这些宏返回 ()
,而不是一个 io::Result
,即使它们执行I/O。这是因为如果写入失败,它们会触发panic。通常,这是可以的:标准流基本上总是存在的。
这里有一个它们不存在的例子
prints_more_than_ten_lines | head
Unix的 head
程序从其标准输入读取十行(默认情况下),将其打印到其标准输出,然后退出。当它退出时,它会关闭其标准流。
当 head
退出时,它关闭管道的读取端。当内核处理管道读取端的 close()
调用,它会向持有管道写入端的过程发送 SIGPIPE
信号(C运行时 crt0
会捕获并终止,但Rust运行时会忽略),然后任何未来的对管道的 write()
调用都会立即返回 -EPIPE
。
Rust的 std::io::Write
函数正确地将此转换为 Err
,然后 println!
解包它,开始panic。
在面临关闭的流时,平静的I/O存储库不会恐慌:它传播错误,并允许调用者优雅地展开并退出。
此存储库公开了四个宏:stdout!
、stdoutln!
、stderr!
和 stderrln!
。这些宏的行为与上面列出的宏完全一样,只是它们返回从 write!
和 writeln!
的 io::Result
,而不是展开它并可能引发恐慌。
此外,此存储库导出了一个函数属性,pipefail
,它仅抑制 BrokenPipe
错误。它可以附加到任何返回 io::Result
的函数(但应仅附加到 main
)。带有 #[pipefail]
的函数其主体周围包裹了一个 match
隔板,它将 Ok(_)
和 Err(io::ErrorKind::BrokenPipe)
都替换为 Ok(())
,并保留所有其他错误不变。
use calm_io::*;
#[pipefail]
fn main() -> std::io::Result<()> {
stdoutln!("Hello stdout from Rust")?;
stderrln!("Hello stderr from Rust")?;
}
作为一个例子,考虑这个对 yes | head
的重新实现
// examples/yeah.rs
use calm_io::*;
#[pipefail]
fn main () -> std::io::Result<!> {
let text = std::env::args().nth(1).unwrap_or("y".to_string());
loop {
stdoutln!("{}", text)?;
}
}
在您的shell中尝试运行这些命令!
$ cargo run --example good_yes | head > /dev/null
$ echo "${PIPESTATUS[@]}"
# The name is `PIPESTATUS` in bash, but `pipestatus` (lowercase!) in zsh
0 0
# yeah exits successfully, head exits successfully
$ yes | head > /dev/null
$ echo "${PIPESTATUS[@]}"
141 0
# yes crashes due to SIGPIPE, head exits successfully
$ cargo run --example bad_yes | head > /dev/null
thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', src/libstd/io/stdio.rs:792:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
将来,可能会添加其他抑制属性,或者创建一个可以接受要抑制的错误列表的通用抑制属性。
依赖项
~1.5MB
~35K SLoC