Rust程式語言的標準函式庫中的「std::fmt」模組提供了多種巨集來格式化或是印出字串。在這個章節中,我們將會學習「format!」、「write!」、「writeln!」、「print!」、「println!」、「eprint!」、「eprintln!」巨集的詳細使用方式。

先來看看以上提及的巨集,它們的用途:

  • format!:可以將文字格式化成String結構實體。
  • write!:可以將格式化之後的文字輸出到某個緩衝空間。
  • writeln!:可以將格式化之後的文字最後再多加一個換行字元輸出到某個緩衝空間。
  • print!:可以將格式化之後的文字輸出到標準輸出(stdout)。
  • println!:可以將格式化之後的文字最後再多加一個換行字元輸出到標準輸出(stdout)。
  • eprint!:可以將格式化之後的文字輸出到標準錯誤(stderr)。
  • eprintln!:可以將格式化之後的文字最後再多加一個換行字元輸出到標準錯誤(stderr)。

格式化文字的用法

Rust程式語言的標準函式庫提供的文字格式化方式,是利用字串定數來撰寫文字格式化的範本(template),接著將字串定數之後傳入的參數代入至範本中由「{}」組成的空位,重新組合成新的字串。編譯器會在編譯階段的時候,就去編譯文字格式化的範本,因此這樣的使用方式對於程式執行階段的運算資源負擔並不大。

文字格式化的範例如下:

幾個比較特別需要注意的地方是,如果要同時使用一般依照位置來填入的參數和依照名稱來填入的參數,依照名稱來填入的參數必須要寫在依照位置來填入的參數之後,否則會編譯錯誤。例如以下程式會編譯錯誤:

「{}」類似「{0}」、「{1}」等用法,只是它不明確指定參數的位置,而是讓編譯器在編譯階段時,由其在範本中從左到右的順序來推斷它的參數位置。也就是說,「{1} {} {2} {} {0} {}」會被視為「{1} {0} {2} {1} {0} {2}」。如果「{}」內有冒號「:」的話,在「:」的左邊可以指定參數明確的位置或是名稱,在「:」的右邊可以使用一些比較特別的格式化方式,列表如下:

  • 無:使用Display特性的「fmt」方法。
  • ?:使用Debug特性的「fmt」方法。
  • x:使用LowerHex特性的「fmt」方法。
  • X:使用UpperHex特性的「fmt」方法。
  • x?:如果參數擁有LowerHex特性,就使用LowerHex的「fmt」方法。否則使用Debug特性的「fmt」方法。
  • X?:如果參數擁有UpperHex特性,就使用UpperHex的「fmt」方法。否則使用Debug特性的「fmt」方法。
  • o:使用Octal特性的「fmt」方法。
  • p:使用Pointer特性的「fmt」方法。
  • b:使用Binary特性的「fmt」方法。
  • e:使用LowerExp特性的「fmt」方法。
  • E:使用UpperExp特性的「fmt」方法。
  • n.m:如同C語言「%n.m」的用法。可以將數值在格式化成文字的同時,去控制其寬度。「n」代表小數點前後文字的寬度(包含小數點本身和正負號),若寬數不足的話會在文字的左邊、右邊,或是左右兩邊以空格或是「0」來填充。如果在「n」的前面加上「0」就是用「0」來填充。如果在「n」的前面加上「>」就是將空格填充到文字的左邊(文字置右)。如果在「n」的前面加上「<」就是將空格填充到文字的右邊(文字置左)。如果在「n」的前面加上「^」就是將空格平均填充到文字的左右兩邊(文字置中)。預設數值型別的參數會置右,其餘型別的參數會置左。「m」代表小數點後數字的位數,參數的數值若小數點後的位數比「m」大的話,會在該位進行「四捨六入五成雙」的運算。這裡要注意的是,如果參數數值不是浮點數型別,「m」是沒有作用的。「n」可以省略,「.m」也可以省略。「n」或「m」的值也可以透過指定參數明確的位置或是名稱來決定,但是參數的位置或是名稱後面必須再加上「$」字元。
  • n.*:類似「n.m」,但這個用法須一次對應兩個參數。第一個參數為「m」,代表小數點後數字的位數。第二個參數才是實際要被格式化的值。這個用法不能指定參數明確的位置或是名稱,但是可以快速地將多個數值組合成文字。

將數值型別的參數代入至範本中時,可以在「:」的右邊插入正號「+」,如此一來,無論該數值是正數還是負數,都會顯示其正負號。而如果在「x」、「X」、「x?」、「X?」、「o」、「b」的左方加上井字號「#」,格式化出來的數值文字會以「0x」、「0o」、「0b」開頭。

如果在「?」前加上井字號「#」,則會美化格式化的文字,舉例來說:

不加上井字號「#」,程式執行結果如下:

Point { x: 0, y: 0 }

加上井字號,程式執行結果如下:

範本中的空位皆是由「{}」組成,那如果我們真的需要在文字中使用到大括號「{}」要怎麼辦呢?那就只能用兩個相同連續的大括號來表示一個大括號文字了,「{{」可以表示「{」,「}}」可以表示「}」。例如:

程式執行結果如下:

{}

format!

write!

writeln!

print!

println!

eprint!

eprintln!

其它會格式化文字的巨集

標準函式庫中有些不在「std::fmt」模組內的巨集也會去格式化文字,例如在前面的章節使用過的「panic!」、「assert!」、「assert_eq!」、「assert_ne!」。

舉例來說:

然而,「std::fmt」模組外的巨集,如果「範本」後面沒有再接上任何的參數,那麼原先應該要被當作是「範本」的字串定數,就只會是一個普通的字串定數。舉例來說:

以上程式會編譯失敗,因為「print!」巨集的範本需要有一個參數。

以上程式會編譯成功,因為「panic!」巨集在「範本」後並沒有加上任何參數,因此這個「範本」會直接以普通的字串定數來對待。

結論

Rust程式語言的標準函式庫提供的巨集時常會用到格式化文字的功能,也因此清楚了解格式化文字的方式將有助於我們使用這些巨集,甚至是撰寫出我們自己的巨集。

在下一章節,我們就是要來學習如何建立出自己的巨集。