格式化是每天都在用的功能,但很多细节容易写错:Display 和 Debug 到底有什么不同、说明符的完整写法、为什么自己实现 Display 之后 {:>8} 不起作用。这篇逐项跑出真实输出。
源码:
1 | #[derive(Debug)] struct P { x: i32, y: i32 }
2 | fn main() {
3 | let p = P { x: 1, y: 2 };
4 | println!("{:?}", p);
5 | println!("{}", p);
6 | }
编译:失败
error[E0277]: `P` doesn't implement `std::fmt::Display`
--> <实验目录>/src/u51a.rs:5:20
|
5 | println!("{}", p);
| -- ^ `P` cannot be formatted with the default formatter
| |
| required by this formatting parameter
|
help: the trait `std::fmt::Display` is not implemented for `P`
--> <实验目录>/src/u51a.rs:1:18
|
1 | #[derive(Debug)] struct P { x: i32, y: i32 }
| ^^^^^^^^
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.derive(Debug) 的展开见derive 自动生成 Trait 实现。
源码:
1 | fn main() {
2 | println!("字符串 Display: {} Debug: {:?}", "a\"b\n", "a\"b\n");
3 | println!("字符 Display: {} Debug: {:?}", '\'', '\'');
4 | println!("浮点 Display: {} Debug: {:?}", 1.0f64, 1.0f64);
5 | println!("浮点 Display: {} Debug: {:?}", 1e21f64, 1e21f64);
6 | println!("Option Debug: {:?}", Some("x"));
7 | println!("Vec Debug: {:?}", vec![Some(1), None]);
8 | println!("元组 Debug: {:?}", (1, "a", 2.5));
9 | println!("{{:#?}} 分行缩进:\n{:#?}", (1, vec![Some("a")]));
10 | }
编译:成功
运行(退出码 0):
字符串 Display: a"b
Debug: "a\"b\n"
字符 Display: ' Debug: '\''
浮点 Display: 1 Debug: 1.0
浮点 Display: 1000000000000000000000 Debug: 1e21
Option Debug: Some("x")
Vec Debug: [Some(1), None]
元组 Debug: (1, "a", 2.5)
{:#?} 分行缩进:
(
1,
[
Some(
"a",
),
],
) 源码:
1 | fn main() {
2 | println!("[{:8}] [{:<8}] [{:>8}] [{:^8}] [{:*^8}] [{:->8}]", "ab", "ab", "ab", "ab", "ab", "ab");
3 | println!("[{:8}] [{:<8}] [{:08}] [{:+}] [{:+08.2}]", 42, 42, 42, 42, 3.14159);
4 | println!("[{:.2}] [{:8.3}] [{:<8.1}] [{:e}] [{:.1e}]", 3.14159, 3.14159, 3.14159, 1234.5, 1234.5);
5 | println!("[{:x}] [{:X}] [{:#x}] [{:#010x}] [{:b}] [{:#b}] [{:o}]", 255, 255, 255, 255, 5, 5, 8);
6 | let w = 6; let p = 2;
7 | println!("[{:w$.p$}] [{:>1$}] [{:.*}]", 2.71828, 7, 3, 1.23456);
8 | println!("位置参数 {0} {1} {0},命名参数 {name}", "a", "b", name = "n");
9 | }
编译:成功
运行(退出码 0):
[ab ] [ab ] [ ab] [ ab ] [***ab***] [------ab]
[ 42] [42 ] [00000042] [+42] [+0003.14]
[3.14] [ 3.142] [3.1 ] [1.2345e3] [1.2e3]
[ff] [FF] [0xff] [0x000000ff] [101] [0b101] [10]
[ 2.72] [ 7] [1.235]
位置参数 a b a,命名参数 n 源码:
1 | use std::fmt;
2 | struct A(&'static str); struct B(&'static str);
3 | impl fmt::Display for A { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } // write!:宽度被忽略
4 | impl fmt::Display for B { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad(self.0) } } // f.pad:遵守宽度与对齐
5 | struct Secret { user: &'static str, pass: &'static str }
6 | impl fmt::Debug for Secret { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Secret").field("user", &self.user).field("pass", &"***").finish() } }
7 | fn main() {
8 | println!("write! 实现:[{:>6}] [{:<6}]", A("ab"), A("ab"));
9 | println!("f.pad 实现:[{:>6}] [{:<6}]", B("ab"), B("ab"));
10 | println!("{:?}", Secret { user: "ann", pass: "hunter2" });
11 | println!("{:#?}", Secret { user: "ann", pass: "hunter2" });
12 | }
编译:成功
运行(退出码 0):
write! 实现:[ab] [ab]
f.pad 实现:[ ab] [ab ]
Secret { user: "ann", pass: "***" }
Secret {
user: "ann",
pass: "***",
}