输出
打印“Hello World”
#![allow(unused)] fn main() { println!("Hello World"); }
好吧,这很简单。很好,继续下一个主题。
使用 println!
您可以使用 println!
宏打印您喜欢的任何内容。这个宏有一些非常强大的功能,但也有一些特殊的语法。它期望您将字符串字面量作为第一个参数,该字符串字面量包含占位符,这些占位符将由作为后续参数提供的值的填充。
例如
#![allow(unused)] fn main() { let x = 42; println!("My lucky number is {}.", x); }
将打印
My lucky number is 42.
上面的字符串中的花括号 ({}
) 是这些占位符之一。这是默认的占位符类型,它尝试以人类可读的方式打印给定值。对于数字和字符串,这非常有效,但并非所有类型都可以做到这一点。这就是为什么还存在“调试表示”,您可以通过像这样填充占位符的花括号来获得它:{:?}
。
例如,
#![allow(unused)] fn main() { let xs = vec![1, 2, 3]; println!("The list is: {:?}", xs); }
将打印
The list is: [1, 2, 3]
如果您希望自己的数据类型可打印以进行调试和记录,您可以在大多数情况下在其定义之上添加 #[derive(Debug)]
。
打印错误
应通过 stderr
打印错误,以便用户和其他工具更容易将输出管道传输到文件或更多工具。
在 Rust 中,这是通过 println!
和 eprintln!
实现的,前者打印到 stdout
,后者打印到 stderr
。
#![allow(unused)] fn main() { println!("This is information"); eprintln!("This is an error! :("); }
关于打印性能的说明
打印到终端出奇地慢!如果您在循环中调用诸如 println!
之类的东西,它很容易成为一个瓶颈,否则是一个快速的程序。为了加快速度,您可以做两件事。
首先,您可能希望减少实际“刷新”到终端的写入次数。println!
告诉系统每次刷新到终端,因为它通常打印每行新内容。如果您不需要这样做,您可以将您的 stdout
处理程序包装在一个 BufWriter
中,该处理程序默认情况下缓冲高达 8 kB。(您仍然可以在此 BufWriter
上调用 .flush()
,当您想立即打印时。)
#![allow(unused)] fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // get the global stdout entity let mut handle = io::BufWriter::new(stdout); // optional: wrap that handle in a buffer writeln!(handle, "foo: {}", 42); // add `?` if you care about errors here }
其次,锁定 stdout
(或 stderr
)并使用 writeln!
直接打印到它会有所帮助。这可以防止系统反复锁定和解锁 stdout
。
#![allow(unused)] fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // get the global stdout entity let mut handle = stdout.lock(); // acquire a lock on it writeln!(handle, "foo: {}", 42); // add `?` if you care about errors here }
您也可以将这两种方法结合起来。
显示进度条
一些 CLI 应用程序运行不到一秒钟,而另一些则需要几分钟或几小时。如果您正在编写后一种类型的程序,您可能希望向用户显示正在发生的事情。为此,您应该尝试打印有用的状态更新,理想情况下以易于理解的形式。
使用 indicatif 板条箱,您可以向您的程序添加进度条和小旋转器。这是一个快速示例
fn main() {
let pb = indicatif::ProgressBar::new(100);
for i in 0..100 {
do_hard_work();
pb.println(format!("[+] finished #{}", i));
pb.inc(1);
}
pb.finish_with_message("done");
}
记录
为了更容易理解我们的程序中发生了什么,我们可能希望添加一些日志语句。这在编写应用程序时通常很容易。但当半年后再次运行此程序时,它将变得非常有用。在某种程度上,记录与使用 println!
相同,只是您可以指定消息的重要性。您通常可以使用的级别是错误、警告、信息、调试和跟踪(错误具有最高优先级,跟踪具有最低优先级)。
要向您的应用程序添加简单记录,您需要两件事:log 板条箱(它包含以日志级别命名的宏)和一个实际将日志输出写入某个有用位置的适配器。能够使用日志适配器非常灵活:例如,您可以使用它们将日志写入终端,还可以写入 syslog 或中央日志服务器。
由于我们现在只关心编写 CLI 应用程序,因此一个易于使用的适配器是 env_logger。它被称为“env”记录器,因为您可以使用环境变量来指定要记录应用程序的哪些部分(以及要记录它们的级别)。它将在您的日志消息前面加上时间戳和日志消息来自的模块。由于库也可以使用 log
,因此您也可以轻松配置它们的日志输出。
这是一个快速示例
use log::{info, warn};
fn main() {
env_logger::init();
info!("starting up");
warn!("oops, nothing implemented!");
}
假设您将此文件作为 src/bin/output-log.rs
,在 Linux 和 macOS 上,您可以像这样运行它
$ env RUST_LOG=info cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
在 Windows PowerShell 中,您可以像这样运行它
$ $env:RUST_LOG="info"
$ cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log.exe`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
在 Windows CMD 中,您可以像这样运行它
$ set RUST_LOG=info
$ cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log.exe`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
RUST_LOG
是您可以用来设置日志设置的环境变量的名称。env_logger
还包含一个构建器,因此您可以以编程方式调整这些设置,例如,默认情况下还显示信息级别消息。
那里有很多替代的日志适配器,以及 log
的替代方案或扩展。如果您知道您的应用程序将有很多要记录的内容,请务必查看它们,并让您的用户的生活更轻松。