一、什么是格式化输出?

Rust 的格式化输出功能强大且灵活,通过println!print!format! 等宏实现。

支持多种占位符(如 {} 、 {:?} 、 {:#?} )和格式化选项(如宽度、精度、对齐方式),能够满足从简单文本输出到复杂数据结构调试的需求。

Rust 还允许通过实现 DisplayDebug 特征来自定义类型格式化行为,同时支持动态精度、宽度控制和元组结构体等高级用法。

二、基础格式化

1. println! 宏基础

src/main.rs 中添加:

rust 复制代码
// 基础字符串输出
println!("Hello, World!");

// 变量插入
let name = "Alice";
println!("Name: {}", name);

// 多变量插入
let age = 30;
println!("{} is {} years old", name, age);

关键特性

  • {} 是占位符,按顺序匹配参数
  • 自动类型推导,无需指定类型
  • 支持任意实现了 Display trait 的类型

2. 基础类型格式化

rust 复制代码
// 数字格式化
let num = 42;
println!("Decimal: {}", num);
println!("Hex: {:x}", num);      // 小写十六进制
println!("Binary: {:b}", num);   // 二进制
println!("Debug: {:?}", num);    // 调试格式

// 浮点数控制
let pi = 3.1415926;
println!("2 decimals: {:.2}", pi);
println!("Scientific: {:e}", pi);

常用格式说明符

  • x/X:十六进制(小写/大写)
  • b:二进制
  • o:八进制
  • e:科学计数法
  • .N:保留 N 位小数

三、高级格式化技巧

1. 对齐与填充

rust 复制代码
// 右对齐填充
println!("{:>10}", "test");  // 输出 "     test"

// 左对齐填充
println!("{:<10}", "test");  // 输出 "test     "

// 零填充
println!("{:0>5}", 42);      // 输出 "00042"

// 符号填充
println!("{:+>6}", 10);      // 输出 "   +10"
println!("{:-<6}", 10);      // 输出 "10----"

2. 宽度与精度控制

rust 复制代码
// 固定宽度
println!("{:5}", 42);        // 输出 "   42"

// 浮点数精度组合
println!("{:10.3}", pi);     // 输出 "     3.142"

// 截断字符串
println!("{:.3}", "Hello");  // 输出 "Hel"

3. 类型强制转换

rust 复制代码
// 显式指定类型
println!("0x{:x}", 255);     // 输出 "0xff"
println!("0o{:o}", 255);     // 输出 "0o377"

// 调试输出
println!("{:?}", (1, "a"));  // 输出 "(1, \"a\")"

四、自定义格式化

1. 实现 Display trait

rust 复制代码
use std::fmt;

struct Point {
    x: i32,
    y: i32,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn main() {
    let p = Point { x: 10, y: 20 };
    println!("Point: {}", p);  // 输出 "Point: (10, 20)"
}

2. 调试输出 (Debug trait)

rust 复制代码
#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}

fn main() {
    let person = Person {
        name: "Bob".to_string(),
        age: 45,
    };
    println!("{:?}", person);  // 输出 "Person { name: "Bob", age: 45 }"
}

3. 自定义格式参数

rust 复制代码
struct Temperature(f32);

impl fmt::Display for Temperature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let precision = f.precision().unwrap_or(2);
        write!(f, "{:.*}°C", precision, self.0)
    }
}

fn main() {
    let temp = Temperature(25.678);
    println!("{}", temp);          // 输出 "25.68°C"
    println!("{:.1}", temp);       // 输出 "25.7°C"
}

五、错误处理与调试

1. 编译时类型检查

rust 复制代码
// 类型不匹配会触发编译错误
// println!("{} {}", 42, "text");  // 错误:类型不匹配

// 显式指定类型
println!("{} {}", 42 as i32, "text");  // 正确

2. 格式化错误处理

rust 复制代码
use std::fmt;

struct MyError;

impl fmt::Debug for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Custom error message")
    }
}

fn main() {
    let err = MyError;
    eprintln!("Error: {:?}", err);  // 输出 "Error: Custom error message"
}

六、性能优化技巧

1. 避免重复格式化

rust 复制代码
// 低效方式
for i in 0..1000 {
    println!("Processing item {}", i);
}

// 高效方式(预格式化)
let format_str = "Processing item {}";
for i in 0..1000 {
    println!(format_str, i);
}

2. 使用 write!

rust 复制代码
use std::io::Write;

fn main() {
    let mut buffer = String::new();
    write!(&mut buffer, "Number: {}, {}", 10, 20).unwrap();
    println!("{}", buffer);  // 输出 "Number: 10, 20"
}

七、实战案例:表格输出

rust 复制代码
use prettytable::{Table, Row, Cell};

fn main() {
    let mut table = Table::new();
    table.add_row(Row::new(["Name", "Age"]));
    table.add_row(Row::new(["Alice", Cell::new("30").align(prettytable::Alignment::RIGHT)]));
    table.add_row(Row::new(["Bob", "25"]));
    table.printstd();
}

Cargo.toml 中添加依赖:

toml 复制代码
[dependencies]
prettytable = "0.8"