输出

打印“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的替换或扩展。 如果你知道你的应用要记录很多,请确保日志清晰明了,让你的用户日子舒服些。