输出
打印“Hello World”
# #![allow(unused_variables)] #fn main() { println!("Hello World"); #}
嗯,很简单。很好,下一个话题。
使用 println
你大概可以用println!
宏打印你喜欢的所有东西。这个宏有一些非常惊人的功能,但也有一个特殊的语法。它期望您编写一个字符串文字作为第一个参数,其中包含由后面参数的参数值填充的占位符。
例如:
# #![allow(unused_variables)] #fn main() { let x = 42; println!("My lucky number is {}.", x); #}
将打印
My lucky number is 42.
上面的字符串中大括号({}
)是这些占位符之一。这是默认的占位符类型,尝试以人类可读的方式打印值。对于数字和字符串,这很好地工作,但并非所有类型都能做到这一点。这就是为什么还存在一个“调试表示法”,就是通过在大括号填充占位符:{:?}
。
例如,
# #![allow(unused_variables)] #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_variables)] #fn main() { println!("This is information"); eprintln!("This is an error! :("); #}
一份打印性能的报告
如果你尝试重复println!
的话,你会发现打印到终端非常缓慢!很容易就成为以快速为目标的程序的瓶颈。要加速,这里有两件你能做的事。
第一,您可能想要减少终端实际的刷新(flush)。而每一个println!
会告诉系统每次都刷新一下终端,因为常见功能就是要打印一个新行。如果你并不需要这些,你可以用一个默认的 8 kB 缓冲BufWriter
,去包裹你的stdout
控制器。(你仍可以调用BufWriter
的.flush()
,若是你想要立即打印的话。)
# #![allow(unused_variables)] #fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // 获得 stdout 实体 let mut handle = io::BufWriter::new(stdout); // 可选: 把 stdout 的 控制权 包裹进一个 buffer writeln!(handle, "foo: {}", 42); // 可加上 `?`, 若你关心错误的话。 #}
第二,获得stdout
(或stderr
)的一个锁也有用,并使用writeln!
直接打印到它。这可以防止系统一遍遍重复对stdout
上锁和解锁。
# #![allow(unused_variables)] #fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // 获得 stdout 实体 let mut handle = stdout.lock(); // 获得它的一个锁 writeln!(handle, "foo: {}", 42); // 可加上 `?`, 若你关心错误的话。 #}
你也可以把两种方法结合起来。
显示一个进度条
一些 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
一样,除了你可以指定信息的重要性,常见的级别有 error, warn, info, debug, 和 trace (error具有最高优先级,trace最低的)。
要将简单的日志记录添加到您的应用程序,你需要两个帮手:log箱子(配有日志级别命名的宏)和一个 适配器(adapter),也就是把日志输出到有用的地方。日志适配器的功能是很灵活:你想想,不仅可以把日志输出到终端,还可以是syslog,或中央日志服务器。
鉴于我们现在只关心编写一个命令行应用,适配器简单用env_logger就好。它的名称叫做env
,因它可能使用环境变量,来指定你命令行想要记录的部分。(当然,还有日志的级别)它会有一个时间戳和来自哪个模块,帮你前缀化你的信息,也可以简单配置日志的输出。
这里有一个简单的例子:
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=output_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="output_log=info" //set the env var for the current session
$ 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,您可以像这样运行它:
$ rem set the env var for the current session
$ set RUST_LOG=output_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
还包含一个构建器,所以你可以用程序调整这个配置,就比如说,默认级别信息为 info 。
有很多日志适配器的替代品,当然还有log
的替换或扩展。
如果你知道你的应用要记录很多,请确保日志清晰明了,让你的用户日子舒服些。