println 的高级用法

println! (及其兄弟, print! )提供了一种方便的机制,用于生成和打印包含动态数据的文本,类似于许多其他语言中的 printf 系列函数。它的第一个参数是一个格式字符串,它指示其他参数应该如何作为文本打印。格式字符串可能包含占位符(包含在 {} 中)以指定应发生替换:

// No substitution -- the simplest kind of format string
println!("Hello World");
// Output: Hello World

// The first {} is substituted with a textual representation of
// the first argument following the format string. The second {}
// is substituted with the second argument, and so on.
println!("{} {} {}", "Hello", true, 42);
// Output: Hello true 42

此时,你可能会问:println! 如何知道将布尔值 true 打印为字符串 true{} 实际上是对格式化程序的指令,应该使用 Display 特性将值转换为文本。这个特性是针对大多数原始的 Rust 类型(字符串,数字,布尔值等)实现的,并且用于面向用户的输出。因此,数字 42 将以十进制形式打印为 42,而不是二进制打印,这是它在内部存储的方式。

那么,我们如何打印实现 Display 的类型,例如 Slices([i32]),vectors(Vec<i32>)或 options(Option<&str>)?没有明确的面向用户的文本表示(即你可以轻易插入一个句子)。为了便于打印这些值,Rust 还具有 Debug 特性,以及相应的 {:?} 占位符。从文档中可以看出:“Debug 应该在面向程序员的调试环境中格式化输出。” 我们来看一些例子:

println!("{:?}", vec!["a", "b", "c"]);
// Output: ["a", "b", "c"]

println!("{:?}", Some("fantastic"));
// Output: Some("fantastic")

println!("{:?}", "Hello");
// Output: "Hello"
// Notice the quotation marks around "Hello" that indicate
// that a string was printed.

Debug 还有一个内置的漂亮打印机制,你可以在冒号后使用 # 修饰符启用:

println!("{:#?}", vec![Some("Hello"), None, Some("World")]);
// Output: [
//    Some(
//        "Hello"
//    ),
//    None,
//    Some(
//        "World"
//    )
// ]

格式字符串允许你表达相当复杂的替换

// You can specify the position of arguments using numerical indexes.
println!("{1} {0}", "World", "Hello");
// Output: Hello World

// You can use named arguments with format
println!("{greeting} {who}!", greeting="Hello", who="World");
// Output: Hello World

// You can mix Debug and Display prints:
println!("{greeting} {1:?}, {0}", "and welcome", Some(42), greeting="Hello");
// Output: Hello Some(42), and welcome

println! 和朋友也会警告你,如果你试图做一些不起作用的事情,而不是在运行时崩溃:

// This does not compile, since we don't use the second argument.
println!("{}", "Hello World", "ignored");

// This does not compile, since we don't give the second argument.
println!("{} {}", "Hello");

// This does not compile, since Option type does not implement Display
println!("{}", Some(42));

在它的核心,Rust 打印宏只是围绕 format! 宏的包装器,它允许通过将不同数据值的文本表示拼接在一起来构造字符串。因此,对于上面的所有示例,你可以用 println! 替换 format! 来存储格式化的字符串而不是打印它:

let x: String = format!("{} {}", "Hello", 42);
assert_eq!(x, "Hello 42");