格式化输出

输出宏

Rust 的打印(输出)功能由 std::fmt 模块中的一系列实现,常用宏说明如下:

  • format!:将格式化文本写入字符串
  • print!:输出内容到控制台(标准输出)
  • println!:同 print!,末尾自动换行
  • eprint!:输出内容到标准错误流
  • eprintln!:同 eprint!,末尾自动换行

凡是有ln结尾的,就是增加了换行符,输出完毕后会换行。

e开头的e,表示error,用来输出错误信息

以上宏的格式化语法完全一致,且 Rust 会在编译阶段校验格式化代码是否合法

使用例子

基础占位符 {}

{} 是通用占位符,会按顺序替换为后续传入参数,并自动转为字符串输出。

fn main() {
    println!("{} days", 31);
    // 输出:31 days
}Code language: JavaScript (javascript)

位置参数

{} 内填写数字索引(从 0 开始),可手动指定使用第几个参数,支持参数复用、打乱顺序。

fn main() {
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
    // 输出:Alice, this is Bob. Bob, this is Alice
}Code language: JavaScript (javascript)

上面例子,可以看到参数的索引可以不从0 1 2 …排序下去的,可以打乱。后面的参数”Alice”, “Bob” ,Alice就是0, Bob就是1 。这样排序下去。

命名参数

如果你觉得上面的通过索引来应用不够直观,可以使用“命名参数”,格式字符串内使用 {参数名},调用时以 参数名=值 形式传参,顺序不受限制,可读性更强。

fn main() {
    println!("{subject} {verb} {object}",
         object="the lazy dog",
         subject="the quick brown fox",
         verb="jumps over");
    // 输出:the quick brown fox jumps over the lazy dog
}Code language: JavaScript (javascript)

相当于,给后面的参数一个名称,然后签名的格式化字符串,你可以用{参数名},来表示,更加直观。

进制格式化(:格式符

在占位符内通过 : 加格式字符,可将数字转为二进制、八进制、十六进制等不同进制。

  • :b:二进制
  • :o:八进制
  • :x:小写十六进制
fn main() {
    println!("十进制:    {}",   69420);    // 69420
    println!("二进制:  {:b}", 69420);    // 10000111100101100
    println!("八进制:  {:o}", 69420);    // 207454
    println!("十六进制:{:x}", 69420);    // 10f2c
}Code language: JavaScript (javascript)

宽度对齐与补位

1 右对齐(>宽度

{:>n} 表示总宽度为 n,内容右对齐,左侧补空格。

fn main() {
	println!("{number:>5}", number=1);
	// 总宽度5,左侧补4个空格 → 输出:    1
}Code language: JavaScript (javascript)
2 补零对齐

在对齐符号前加 0,使用数字 0 替代空格补位。

  • :0>5:右对齐,左侧补 0
  • :0<5:左对齐,右侧补 0
fn main() {
	println!("{number:0>5}", number=1); // 右对齐补0 → 00001
	println!("{number:0<5}", number=1); // 左对齐补0 → 10000
}Code language: JavaScript (javascript)

还可以用其他任何的字符的,不一定是0,例如下面例子,签名补大写字母A

fn main() {
	println!("{number:A>5}", number=1); // 右对齐补0 → 00001
	println!("{number:A<5}", number=1); // 左对齐补0 → 10000
}Code language: JavaScript (javascript)
3 动态宽度(变量名$

宽度不再写死,通过 变量名$ 引用外部命名参数 / 变量,实现动态控制宽度。

也就是宽度,可以通过变量,来控制,而不是代码固定写死。

fn main() {
	// 方式1:配合命名参数
	println!("{number:0>width$}", number=1, width=8);

	// 方式2:直接引用代码内变量(Rust 1.58+ 支持)
	let number: f64 = 1.0;
	let width: usize = 8;
	println!("{number:>width$}"); // 输出:    1
}Code language: JavaScript (javascript)

编译期参数校验

Rust 会在编译阶段检查占位符数量和传入参数数量是否匹配,参数缺失直接编译报错。

fn main() {
	// 2个占位符,但只传1个参数,编译失败
	println!("My name is {0}, {1} {0}", "Bond");
	// 修正:补充参数 "James"
	println!("My name is {0}, {1} {0}", "Bond", "James");
}Code language: JavaScript (javascript)

编译提示,{1}位置,无效,因为后面只有一个参数。

错误:引用了不存在的位置参数 1(当前仅传入 1 个参数)
 --> D:\rustdemo\hello.rs:3:29
  |
3 |     println!("My name is {0}, {1} {0}", "Bond");
  |                                ^
  |
  = 提示:位置参数从 0 开始编号

错误:编译终止,共 1 个错误Code language: JavaScript (javascript)

自定义类型格式化限制

默认情况下,{} 只能格式化实现了 fmt::Display 特征的类型。 自定义结构体默认未实现该特征,直接使用 {} 打印会编译报错。

fn main() {
    struct Structure(i32);
	// 下方代码无法编译
    println!("{}", Structure(3));
}Code language: JavaScript (javascript)
错误[E0277]:结构体 `Structure` 未实现 `std::fmt::Display` 特征
 --> D:\rustdemo\hello.rs:4:20
4 |     println!("{}", Structure(3));
  |               --   ^^^^^^^^^^^^ 该类型无法使用默认格式器输出
  |               |
  |               此处格式参数要求类型实现 Display 特征

提示:`Structure` 结构体没有实现 `std::fmt::Display` 特征
 --> D:\rustdemo\hello.rs:2:2
2 |     struct Structure(i32);
  |     ^^^^^^^^^^^^^^^^

补充说明:格式字符串中,可以尝试改用 `{:?}`(或 `{:#?}` 美化打印)
编译终止,共 1 处错误。

如需查看该错误详情,可执行命令:rustc --explain E0277Code language: JavaScript (javascript)

* 1 如果您需要输出结构体,则需要实现Display特征(暂时理解就可以,后面会学习)

use std::fmt;

struct Structure(i32);

// 为结构体实现 Display 特征
impl fmt::Display for Structure {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // self.0 访问元组结构体内部的 i32 值
        write!(f, "数值:{}", self.0)
    }
}

fn main() {
    println!("{}", Structure(3));
}Code language: PHP (php)
* 2 临时调试 → 使用调试格式 {:?} / {:#?}

适合程序员调试用,如果你不是正式环境,只是为了调试这个结构体里面的值是什么,你可以使用调试特性Debug

Rust 为自定义类型默认实现了调试特征 Debug,用 {:?} 即可打印:

#[derive(Debug)]
#[allow(dead_code)] // 屏蔽未使用代码/字段警告
struct Structure(i32);

fn main() {
    println!("{:?}", Structure(3));
    println!("{:#?}", Structure(3));
}Code language: PHP (php)

* std::fmt 两大核心格式化特征

std::fmt 模块包含多个控制文本展示的特征,常用两个:

  1. fmt::Debug 搭配占位符 {:?},专门用于调试场景输出内容。
  2. fmt::Display 搭配占位符 {},以更规范、友好的形式展示内容,面向普通用户。

标准库内置类型已默认实现这两个特征,可直接打印;自定义类型无法直接使用,需要手动实现对应特征。

为类型实现 fmt::Display 后,会自动附带实现 ToString 特征,支持将该类型转为字符串。

#[allow(dead_code)]

#[allow(dead_code)] 是代码属性,作用于紧随其后的代码项,用来屏蔽 “代码未使用” 的警告

上面的例子

fn main() {
    struct Structure(i32);
	// 下方代码无法编译
    //println!("{}", Structure(3));
}Code language: JavaScript (javascript)

由于第二句代码println!(“{}”, Structure(3)); 会报错,注释了,如果这个时候执行,

warning: struct `Structure` is never constructed
 --> D:\rustdemo\hello.rs:2:12
  |
2 |     struct Structure(i32);
  |            ^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default

warning: 1 warning emitted
Code language: JavaScript (javascript)

会有这样的警告,因为这句代码,struct Structure(i32); 从来没使用。如果你想屏蔽这个警告,可以增加这个标注

fn main() {
	#[allow(dead_code)] 
    struct Structure(i32);
	// 下方代码无法编译
    //println!("{}", Structure(3));
}Code language: PHP (php)
对于浮点数,保留小数

定义变量 pi = 3.141592,编写格式化输出,保留三位小数,打印内容:Pi is roughly 3.142

fn main() {
	let pi = 3.141592;
	println!("Pi is roughly {:.3}", pi);//3.142
	println!("Pi is roughly {0}", pi); //3.141592
}Code language: JavaScript (javascript)

Previous:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注