格式化輸出

輸出巨集

Rust 的列印(輸出)功能,是由 std::fmt 模組內的一連串巨集所實作,以下說明常用的巨集:

  • format!:將格式化文字寫入字串
  • print!:將內容輸出至主控台(標準輸出)
  • println!:用法同 print!,結尾會自動換行
  • eprint!:將內容輸出至標準錯誤串流
  • eprintln!:用法同 eprint!,結尾會自動換行

名稱結尾帶有 ln 的巨集,都會自動加上換行符號,輸出完畢後換行。

開頭為 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); // 靠右對齊並補零 → 00001
	println!("{number:0<5}", number=1); // 靠左對齊並補零 → 10000
}Code language: JavaScript (javascript)

除了數字 0 以外,也可以使用其他字元補位。以下範例使用大寫英文字母 A 來填補空位。

fn main() {
	println!("{number:A>5}", number=1); // 靠右對齊補 A → AAAAA1
	println!("{number:A<5}", number=1); // 靠左對齊補 A → 1AAAAA
}Code language: PHP (php)
3 動態寬度(變數名稱$

不用把寬度寫死,透過 變數名稱$ 參考外部的具名參數或變數,就能動態控制顯示寬度。

也就是顯示寬度可由變數控制,而非直接在程式碼中固定數值。

fn main() {
	// 方式一:搭配具名參數
	println!("{number:0>width$}", number=1, width=8);

	// 方式二:直接取用區域變數(Rust 1.58 以上版本支援)
	let number: f64 = 1.0;
	let width: usize = 8;
	println!("{number:>width$}"); // 輸出:    1
}Code language: JavaScript (javascript)

編譯期參數檢查

Rust 會在編譯階段檢查佔位符數量與傳入參數數量是否一致,若參數不足,會直接產生編譯錯誤。

fn main() {
	// 兩個佔位符,但只傳入一個參數,編譯失敗
	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)

由於列印程式碼會報錯而被註解,此時執行程式,

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)

編譯器就會出現以上警告,原因是結構體 Structure 定義後從未被使用。如果想要隱藏這則警告,就可以加上這個標註。

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:

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *