格式化輸出#

Rust 的 println! 巨集使用格式字串控制輸出的樣式,格式字串中的 {} 是佔位符。


1. 基本輸出#

{} 依序填入後面的參數:

1
2
3
4
5
fn main() {
    let name = "Rust";
    let year = 2015;
    println!("{} 誕生於 {} 年", name, year); // Rust 誕生於 2015 年
}

Rust 1.58 以後,可以在 {} 裡直接寫變數名稱:

1
2
3
4
fn main() {
    let x = 42;
    println!("{x}"); // 42
}

{}只能放變數名稱 ,不能放方法呼叫或運算式,需要計算的值必須作為獨立參數傳入:

1
2
3
4
5
6
7
fn main() {
    let s = String::from("hello");

    println!("{}", s.len());          // 正確:5
    println!("{}", s.to_uppercase()); // 正確:HELLO
    // println!("{s.len()}");         // 編譯錯誤!
}

這和 Python f-string(f"{s.len()}")或 Kotlin("${s.length}")不同——Rust 格式字串不支援任意表達式。


2. 格式符#

格式符寫在 {} 的冒號後面,語法為 {變數:格式符},例如 {n:b} 表示以二進位輸出變數 n

進位#

:b:o:x:X 分別輸出二進位、八進位、十六進位;加上 # 前綴會顯示進位標記:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn main() {
    let n = 255;
    println!("{n}");    // 255       (十進位)
    println!("{n:b}");  // 11111111  (二進位)
    println!("{n:o}");  // 377       (八進位)
    println!("{n:x}");  // ff        (十六進位小寫)
    println!("{n:X}");  // FF        (十六進位大寫)
    println!("{n:#b}"); // 0b11111111(加前綴)
    println!("{n:#o}"); // 0o377
    println!("{n:#x}"); // 0xff
}

浮點數精度#

:.N 指定小數點後幾位,多餘的位數會四捨五入:

1
2
3
4
5
6
7
fn main() {
    let f = 3.14159;
    println!("{f}");     // 3.14159(預設,保留全部)
    println!("{f:.0}");  // 3
    println!("{f:.2}");  // 3.14
    println!("{f:.4}");  // 3.1416(四捨五入)
}

精度也可以和欄位寬度組合:{:8.2} 表示欄寬 8、小數點後 2 位:

1
2
3
fn main() {
    println!("{:8.2}", 3.14159); // "    3.14"(欄寬 8,靠右)
}

欄位寬度與對齊#

數字指定最小欄位寬度,不足的地方補空白。 < 靠左、> 靠右(數字預設靠右、字串預設靠左)、^ 置中:

1
2
3
4
5
6
7
8
fn main() {
    println!("{:<10}", "hello");  // "hello     "(靠左)
    println!("{:>10}", "hello");  // "     hello"(靠右)
    println!("{:^10}", "hello");  // "  hello   "(置中)

    println!("{:>8}", 42);        // "      42" (靠右)
    println!("{:<8}", 42);        // "42      " (靠左)
}

用指定字元填充(把填充字元放在對齊符號前):

1
2
3
4
fn main() {
    println!("{:0>5}", 42);    // "00042"(補零,靠右)
    println!("{:*^10}", "hi"); // "****hi****"(補星號,置中)
}

Debug 格式#

:? 使用 Debug 格式,可以印出陣列、元組等複合型別;:#? 加上縮排換行,結構複雜時更易閱讀:

1
2
3
4
5
6
7
fn main() {
    let arr = [1, 2, 3];
    println!("{arr:?}");  // [1, 2, 3]

    let tup = (42, "hello", true);
    println!("{tup:?}");  // (42, "hello", true)
}

自訂結構體需加上 #[derive(Debug)] 屬性才能使用 :?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#[derive(Debug)]
struct Point { x: i32, y: i32 }

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("{p:?}");  // Point { x: 1, y: 2 }
    println!("{p:#?}"); // 格式化縮排:
                        // Point {
                        //     x: 1,
                        //     y: 2,
                        // }
}

速查表#

格式符意義輸出範例
{}預設顯示255
{:b}二進位11111111
{:o}八進位377
{:x}十六進位小寫ff
{:X}十六進位大寫FF
{:#x}十六進位加前綴0xff
{:.2}小數點後 2 位3.14
{:8}欄寬 8,靠右255
{:<8}欄寬 8,靠左255
{:^8}欄寬 8,置中255
{:08}欄寬 8,補零00000255
{:?}Debug 格式[1, 2, 3]
{:#?}Debug 縮排格式多行縮排輸出

Reference#

https://doc.rust-lang.org/std/fmt/index.html