格式化打印

打印功能由 std::fmt 中定义的一系列处理,其中包括:

  • format!:将格式化文本写入 String
  • print!:与 format! 类似,但文本会打印到控制台(io::stdout)
  • println!:与 print! 类似,但会在末尾添加换行符
  • eprint!:与 print! 类似,但文本会打印到标准错误输出(io::stderr)
  • eprintln!:与 eprint! 类似,但会在末尾添加换行符

所有这些宏都以相同的方式解析文本。此外,Rust 会在编译时检查格式化的正确性。

fn main() {
    // 通常,`{}` 会被自动替换为任何参数。
    // 这些参数会被转换为字符串。
    println!("{} 天", 31);

    // 可以使用位置参数。在 `{}` 中指定一个整数
    // 来决定替换哪个额外的参数。参数编号
    // 从格式字符串后立即开始,从 0 开始。
    println!("{0},这是 {1}。{1},这是 {0}", "Alice", "Bob");

    // 还可以使用命名参数。
    println!("{subject} {verb} {object}",
             object="那只懒惰的狗",
             subject="那只敏捷的棕色狐狸",
             verb="跳过");

    // 在 `:` 后指定格式字符,
    // 可以调用不同的格式化方式。
    println!("十进制:               {}",   69420); // 69420
    println!("二进制:               {:b}", 69420); // 10000111100101100
    println!("八进制:               {:o}", 69420); // 207454
    println!("十六进制:             {:x}", 69420); // 10f2c

    // 可以指定宽度来右对齐文本。这将输出
    // "    1"。(四个空格和一个 "1",总宽度为 5。)
    println!("{number:>5}", number=1);

    // 可以用额外的零来填充数字,
    println!("{number:0>5}", number=1); // 00001
    // 通过翻转符号来左对齐。这将输出 "10000"。
    println!("{number:0<5}", number=1); // 10000

    // 在格式说明符后添加 `$` 可以使用命名参数。
    println!("{number:0>width$}", number=1, width=5);

    // Rust 甚至会检查使用的参数数量是否正确。
    println!("我的名字是 {0},{1} {0}", "Bond");
    // FIXME ^ 添加缺失的参数:"James"

    // 只有实现了 fmt::Display 的类型才能用 `{}` 格式化。
    // 用户定义的类型默认不实现 fmt::Display。

    #[allow(dead_code)] // 禁用 `dead_code`,它会警告未使用的模块
    struct Structure(i32);

    // 这无法编译,因为 `Structure` 没有实现 fmt::Display。
    // println!("这个结构体 `{}` 无法打印...", Structure(3));
    // TODO ^ 尝试取消注释这一行

    // 在 Rust 1.58 及以上版本,你可以直接从周围的变量捕获参数。
    // 就像上面一样,这将输出 "    1",4 个空格和一个 "1"。
    let number: f64 = 1.0;
    let width: usize = 5;
    println!("{number:>width$}");
}

std::fmt 包含许多控制文本显示的 traits。下面列出了两个重要的基本形式:

  • fmt::Debug: 使用 {:?} 标记。用于调试目的的文本格式化。
  • fmt::Display: 使用 {} 标记。以更优雅、用户友好的方式格式化文本。

这里我们使用 fmt::Display,因为标准库为这些类型提供了实现。要打印自定义类型的文本,需要额外的步骤。

实现 fmt::Display 特性会自动实现 ToString 特性,这允许我们将该类型转换String

43 行#[allow(dead_code)]是一个 属性(attribute),它只适用于它之后的模块。

练习

  • 修复上述代码中的问题(参见 FIXME 注释),使其能够正常运行。
  • 尝试取消注释那行尝试格式化 Structure 结构体的代码(参见 TODO 注释)
  • 添加一个 println! 宏调用,打印:Pi 约等于 3.142,通过控制显示的小数位数来实现。 在本练习中,使用 let pi = 3.141592 作为 pi 的近似值。(提示:你可能需要查阅 std::fmt 文档来了解如何设置显示的小数位数)

另请参阅:

std::fmtmacrosstructtraitsdead_code