DAILY DOCDAILY DOC
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
  • rust

    • Rust
    • add
    • 属性(attributes)
    • cargo issue
    • cli
    • build.rs
    • Enums
    • eventEmitter(rust)
    • 格式化输出 std::fmt
    • rust iterator
    • rust 学习计划
    • 生命周期(lifetime)
    • Linked List
    • log
    • macros
    • mem::size_of
    • niche optimization
    • Rust 所有权
    • 模式匹配(pattern matching)
    • module system
    • result & option
    • .rust-analyzer.json
    • rust startup
    • rust-test
    • 可见性(visibility)
    • cargo
    • toml

格式化输出 std::fmt

1. 基础用法

基础的格式化输出主要使用 print!、println! 和 format! 宏。这些宏可以接受一个格式字符串和一个或多个值。

fn main() {
    let name = "Alice";
    let age = 30;

    println!("Hello, {}!", name);
    println!("{} is {} years old.", name, age);
}

2. 格式字符串

格式字符串包含普通文本和由花括号包围的格式说明符 {}。每个 {} 将会被后面的相应参数替换。

fn main() {
    let a = 1;
    let b = 2;

    println!("a: {}, b: {}", a, b);
}

3. 格式说明符

格式说明符可以指定如何格式化值。常见的格式说明符包括:

  • {:?}:使用 Debug 格式化。
  • {:#?}:使用多行 Debug 格式化。
  • {}:使用 Display 格式化。
fn main() {
    let v = vec![1, 2, 3];

    println!("Debug: {:?}", v);
    println!("Pretty Debug: {:#?}", v);
}

4. 常用格式选项

格式选项允许你进一步控制输出格式。格式说明符可以包含多个选项,如填充、对齐、宽度、精度和类型。

4.1 填充和对齐

你可以指定填充字符和对齐方式:

  • <:左对齐
  • >:右对齐
  • ^:居中对齐
fn main() {
    const LEN: usize = 8; // 使用 usize 代替 u8,因为格式化宽度需要 usize 类型
    println!("{:*<1$}", "left", LEN); // 使用 '*' 填充,左对齐
    println!("{:*>1$}", "right", LEN); // 使用 '*' 填充,右对齐
    println!("{:*^1$}", "center", LEN); // 使用 '*' 填充,居中对齐
    println!("{:*<1$}", "left", LEN); // 使用 '*' 填充,左对齐
}

4.2 宽度和精度

宽度指定最小字段宽度,精度用于浮点数和字符串的最大字符数。

fn main() {
    println!("{:8}", "hello");    // 最小宽度为 8
    println!("{:.3}", "hello");   // 最大宽度为 3
    println!("{:8.3}", "hello");  // 最小宽度为 8,最大宽度为 3

    let pi = 3.141592;
    println!("{:.2}", pi);        // 保留 2 位小数
}

4.3 类型

  • b:二进制格式
  • x:十六进制格式(小写)
  • X:十六进制格式(大写)
  • o:八进制格式
  • p:指针格式
fn main() {
    let num = 255;

    println!("Binary: {:b}", num);
    println!("Hexadecimal: {:x}", num);
    println!("Octal: {:o}", num);
    println!("Pointer: {:p}", &num);
}

5. 自定义格式化

你可以通过实现 std::fmt::Display 或 std::fmt::Debug trait 来定义自定义类型的格式化方式。

5.1 实现 Display trait

Display 用于用户友好的格式化输出。

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: 1, y: 2 };
    println!("Point: {}", p);
}

5.2 实现 Debug trait

Debug 用于调试友好的格式化输出。

use std::fmt;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("Point: {:?}", p);
}

6. format! 宏

format! 宏类似于 println!,但它返回格式化后的字符串,而不是输出到控制台。

fn main() {
    let name = "Alice";
    let greeting = format!("Hello, {}!", name);
    println!("{}", greeting);
}

7. 高级用法

7.1 动态格式化参数

使用 format_args! 宏,可以动态生成格式字符串和参数。

use std::fmt::Write;

fn main() {
    let mut output = String::new();
    write!(&mut output, "Hello, {}!", "world").unwrap();
    println!("{}", output);
}

7.2 自定义格式化函数

你可以定义一个接受 fmt::Formatter 参数的函数,以自定义复杂格式化逻辑。

use std::fmt;

fn custom_format(f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Custom formatted string")
}

fn main() {
    println!("{}", format_args!("{}", custom_format));
}

8. 更多高级用法和技巧

8.1 条件格式化

在实际开发中,可能会遇到需要根据某些条件动态调整格式化输出的情况。

fn main() {
    let number = 42;
    let formatted = if number < 10 {
        format!("{:02}", number)  // 用零填充
    } else {
        format!("{}", number)
    };

    println!("Formatted number: {}", formatted);
}

在这个例子中,根据number的值选择不同的格式化方式。

8.2 多行格式化

有时你需要将格式化结果分成多行输出,这在输出结构化数据时特别有用。

fn main() {
    let name = "Alice";
    let age = 30;
    let address = "1234 Wonderland";

    let info = format!(
        "Name: {}\nAge: {}\nAddress: {}",
        name, age, address
    );

    println!("User Info:\n{}", info);
}

8.3 捕获格式化结果

你可以使用format!宏将格式化结果捕获到字符串中,以便后续处理。

fn main() {
    let name = "Alice";
    let greeting = format!("Hello, {}!", name);
    // 现在 `greeting` 变量包含了格式化后的字符串,可以进行进一步处理
    println!("{}", greeting);
}

8.4 格式化多个值

在某些情况下,你可能需要格式化多个值并将它们组合成一个字符串。

fn main() {
    let width = 4;
    let height = 7;
    let area = width * height;

    let description = format!(
        "The rectangle is {} units wide and {} units high, with an area of {} square units.",
        width, height, area
    );

    println!("{}", description);
}

8.5 自定义格式化宏

如果你经常需要使用特定的格式化逻辑,可以创建自定义宏来简化代码。

macro_rules! greet {
    ($name:expr) => {
        format!("Hello, {}!", $name)
    };
}

fn main() {
    let greeting = greet!("Alice");
    println!("{}", greeting);
}

8.6 实现 fmt::Display 和 fmt::Debug 的高级用法

为了更加灵活地处理复杂的数据结构,可以深入了解和使用fmt::Display和fmt::Debug的高级特性。

use std::fmt;

struct Complex {
    real: f64,
    imag: f64,
}

impl fmt::Display for Complex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} + {}i", self.real, self.imag)
    }
}

impl fmt::Debug for Complex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Complex {{ real: {}, imag: {} }}", self.real, self.imag)
    }
}

fn main() {
    let complex = Complex { real: 3.3, imag: 7.2 };
    println!("Display: {}", complex);
    println!("Debug: {:?}", complex);
}

参考资料

  1. Rust 官方文档:格式化输出
  2. Rust By Example:格式化输出
  3. The Rust Programming Language:格式化输出章节 \
Last Updated:
Contributors: rosendo
Prev
eventEmitter(rust)
Next
rust iterator