[Rust 筆記] format- 宏使用心得 - 彙總
假期,我嘗試使用rust做一款命令行工具,來磨礪自己的rust技術熟練度。起初,面對各式各樣的字符串格式化功能點,我傻乎乎地嘗試自己造輪子。但,實在是遇到了太多技術難點(可難死我了)。後來,通過在論壇發貼請教,我才瞭解到【標準庫 - format!語法擴展】已經90%地滿足了我的需求。至於,剩餘10%的功能,可通過實現不同的format trait來深度定製 - 達成。
於是,我修改了假期目標爲:
-
重新複習
format!宏相關知識點。相比於之前(真是學過N遍了),我要提高對這塊知識點的重視程度。在複習過程,做些歸納總結和條理清晰的筆記。 -
基於【標準庫 -
format!語法擴展】與【實現format trait深度定製】的手段,來做一款rust命令行工具。而不是,自己重新造輪子。
下面即是我對第一個目標的達成成果(除了豐富的教條總結歸納,還有30個例程) --- 掌握rust,先做 “教條俠”。第二個目標還在進行中...
依賴關係宏觀架構圖
宏調用格式
format!("以{parameter}爲佔位符的·格式化字符串·字面量", arguments...)
-
{parameter}名曰Formatting Argument -
argument名曰Value Argument
Formatting Argument格式
下文中的
[...]結構表示
一對
[與]之間的內容是可有可無的。而且
[...]結構是可多層嵌套的。
{[argument][:format-spec]}
-
argument:【引用指令】表示如何找到Value Argument -
format-spec:【格式化指令】表示如何格式化Value Argument爲字符串。
Formatting argument -> argument以如下三種形式引用Value Argument
-
{}名曰:Next (Value) Argument -
【順序訪問】不會受【隨機訪問】· 跳躍式尋找
Value Arguments的影響,因爲前者維護了獨立的【遊標變量】來跟蹤迭代位置。[例程 1] -
順序訪問
(Value) Argument,逐一遍歷arguments列表中的每一項。但是,當format-spec內包含.*時,則會一次迭代遍歷兩項(下文有詳細描述)。 -
Value Argument:無要求
-
{positional parameter}名曰:【索引】(Value) Argument -
positional parameter是integer值。 -
以始於
0的索引值,隨機訪問(Value) Argument。 -
Value Argument:無要求
-
{naming parameter}名曰:【具名】(Value) Argument[例程 2] -
要麼,出現於
Value Arguments列表的末端;格式:<parameter name>=<value>。 -
要麼,不出現在
Value Arguments列表中。相反,編譯器會 -
在當前作用域內,
-
尋找同名綁定變量,
-
使用該綁定變量的值。(餒餒地逆天了)
-
naming parameter是identifier字符串。 -
經由【參數名】,隨機訪問
(Value) Argument。 -
Value Argument:
Formatting argument -> format-spec以如下五種形式進一步格式化Value Argument
下文中的
[...]結構表示
一對
[與]之間的內容是可有可無的。而且
[...]結構是可多層嵌套的。
-
字符串 - 寬度定製
若
mini-width與max-length同時指定,並且mini-width大於max-length,那麼 [例程 8]std::fmt::Display::to_string()成員方法將Value Argument序列化爲字符串。 -
-
padding-char名曰:“填充” -
align名曰:“對齊”若對齊未生效(比如,對
Debug trait實例),那就 -
mini-width名曰:“最小寬度” -
max-length名曰:最多顯示字符數(從左向右截取)[例程 6] -
缺省。即,【空格】填充。
-
<左 (默認) -
^中 -
>右 -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,沒有限制。
-
要麼,數字字面量;
-
要麼,
$後綴【索引值】引用某個Value argument值 [例程 4] -
要麼,
$後綴【具名變量】引用 [例程 5] -
被格式化的值是後一個
Value argument值 -
而被格式化值前面(或左側)的
Value argument代表最多顯示字符數 -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,顯示全部字符。
-
要麼,數字字面量;
-
要麼,
$後綴【索引值】引用某個Value argument值 -
要麼,
$後綴【具名變量】引用 -
要麼,
.*表示當前Formatting argument同時關聯了兩個Value argument[例程 7] -
{[<integer | identifier>][:[[[<padding-char>]<align>]<mini-width>][.<max-length>]]}[例程 3]
-
先使用
max-length截斷字符串 -
再使用
mini-width對截斷後的字符串有填充與對齊處理 -
先 · 普通格式化
Value argument, -
再 · 對結果字符串做 · 對齊 · 格式化處理。
-
數字 - 寬度定製
就數字格式化而言,【正負號】與【進制符】都被計入總寬度內,並擠佔了【佔位符】的 “坑位”。[例程 16]
std::fmt::Display::to_string()成員方法將Value Argument序列化爲字符串。 -
-
padding-char名曰:填充 -
align名曰:對齊若對齊未生效(比如,對
Debug trait實例),那就 -
sign名曰:正負號 -
0名曰:填充0數字 -
mini-width名曰:最小寬度 -
precision名曰:精度 [例程 14] -
padding-char指定整個數字(含正負號)前後的填充符。 -
padding-char填充符可以是任意字符。 -
padding-char的填充優先級低於0[例程 10] -
缺省。即,【空格】填充。
-
與
sign後的0填充符作用不同, -
<左 -
^中 -
>右 (默認) -
要麼,缺省。即,按需顯示
-號 -
要麼,
+。即,總是顯示+/-號 -
0表示在【正負號】與【有效數字】之間以數字0加以填充 `。 -
填充符號僅能是數字
0 -
0填充優先級高於padding-char[例程 11] -
與
padding-char填充符作用不同, -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,沒有限制。
-
要麼,數字字面量。
-
要麼,
$後綴【索引值】引用某個Value argument值 [例程 12] -
要麼,
$後綴【具名變量】引用 [例程 13] -
被格式化的值是後一個
Value argument -
而在被格式值前面(左側)的
Value argument代表精度 -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,沒有限制。
-
要麼,數字字面量;
-
要麼,
$後綴【索引值】引用某個Value argument值 -
要麼,
$後綴【具名變量】引用 -
要麼,
.*表示當前Formatting argument同時關聯了兩個Value argument[例程 15] -
{[<integer | identifier>][:[[[<padding-char>]<align>][<sign>][0]<mini-width>][.<precision>]]}[例程 9]
-
先 · 普通格式化
Value argument, -
再 · 對結果字符串做 · 對齊 · 格式化處理。
-
數字 - 進制轉換 + 有進制符前綴 + 寬度定製
就數字格式化而言,【正負號】與【進制符】都被計入總寬度內,並擠佔了【佔位符】的 “坑位”。[例程 22]
任何實現了
Format trait的【自定義 - 數據類型】的實例都能被format-spec指令序列化與格式化。標準庫已經爲基本數據類型提供了
Format trait的默認實現。 -
-
padding-char名曰:填充 -
align名曰:對齊若對齊未生效(比如,對
Debug trait實例),那就 -
sign名曰:正負號 -
#名曰:進制換算指令。 -
0名曰:填充0數字 -
mini-width名曰:最小寬度 -
numeration名曰:進制符 -
-
padding-char指定整個數字(含 · 正負號 · 與 · 進制符前綴0x,0o,0b·)前後的填充符。 -
padding-char填充符可以是任意字符。 -
padding-char的填充優先級低於#0[例程 18] -
缺省。即,【空格】填充。
-
與
#後0填充符的作用不同, -
<左 -
^中 -
>右 (默認) -
要麼,缺省。按需顯示
-號 -
要麼,
+。即,總是顯示正負號。 -
與末尾處的
numeration參數配套出現。 -
#0表示在【進制符前綴0x,0o,0b】與【有效數字】之間以數字0加以填充 `。 -
填充符號僅能是數字
0 -
#0填充優先級高於padding-char[例程 19] -
與
padding-char填充符作用不同, -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,沒有限制。
-
要麼,數字字面量。
-
要麼,
$後綴【索引值】引用某個Value argument值 [例程 20] -
要麼,
$後綴【具名變量】引用 [例程 21] -
{[<integer | identifier>][:[[<padding-char>]<align>][<sign>]#[[0]<mini-width>]<numeration>]}[例程 17]
-
先 · 普通格式化
Value argument, -
再 · 對結果字符串做 · 對齊 · 格式化處理。
-
數字 - 進制轉換 + 無進制符前綴 + 寬度定製
就數字格式化而言,【正負號】與【進制符】都被計入總寬度內,並擠佔了【佔位符】的 “坑位”。[例程 27]
任何實現了
Format trait的【自定義 - 數據類型】的實例都能被format-spec指令序列化與格式化。標準庫已經爲基本數據類型提供了
Format trait的默認實現。 -
padding-char名曰:填充 -
align名曰:對齊若對齊未生效(比如,對
Debug trait實例),那就 -
sign名曰:正負號 -
0名曰:填充0數字 -
mini-width名曰:最小寬度 -
numeration名曰:進制符 -
-
padding-char指定整個數字(含正負號)前後的填充符。 -
padding-char填充符可以是任意字符。 -
padding-char的填充優先級低於0[例程 23] -
缺省。即,【空格】填充。
-
與
sign後0填充符作用不同, -
<左 -
^中 -
>右 (默認) -
要麼,缺省。按需顯示
-號 -
要麼,
+。即,總是顯示正負號 -
0表示在【正負號】與【有效數字】之間以數字0加以填充。 -
填充符號僅能是數字
0 -
0填充優先級高於padding-char[例程 24] -
與
padding-char填充符作用不同, -
某個
Value argument值 -
或,當前作用域內,某個綁定變量的值
-
要麼,缺省。即,沒有限制。
-
要麼,數字字面量。
-
要麼,
$後綴【索引值】引用某個Value argument值 [例程 25] -
要麼,
$後綴【具名變量】引用 [例程 26] -
{[<integer | identifier>][:[[[<padding-char>]<align>][<sign>][[0]<mini-width>]]<numeration>]}
-
先 · 普通格式化
Value argument, -
再 · 對結果字符串做 · 對齊 · 格式化處理。
-
有縮進格式化
-
適用於複合數據結構。比如,
Vec<T>和HashMap<K, V>。 -
{[<integer | identifier>][:#?]}[例程 28]
在 · 格式化字符串 · 字面量內,轉義錄入{與}字面量
-
轉義
{爲{{ -
轉義
}爲}}
Value argument兩種語法錯誤形式
-
Value argument未被任何Formatting Argument所引用 -
【索引】
(Value) Argument或Next (Value) Argument出現於【具名】(Value) Argument之後。[例程 29]
使format-spec格式化指令對自定義數據類型(的實例)起作用
技術手段就是給【自定義數據類型】實現各種Format trait,從std::fmt::Display與std::fmt::Debug到std::fmt::Octal等等一個都別落下。[例程 30]
但是,有兩個點值得一聊:
-
Format trait默認實現已經幫助開發者完成了開發者僅需調用
std::fmt::Formatter的成員方法(比如,std::fmt::Formatter::fill(&self))就可獲取格式化指令的具體值。 -
對【格式化字符串 · 字面量】的解析處理
-
和,對
format-spec指令值的提取工作 -
雖然 “抽象” 成員方法
fn fmt(...) -> std::fmt::Result的返回值類型是Result,但是fn fmt()不應該將format trait業務實現代碼的 “本地” 錯誤僞裝成std::fmt::Result返回。因爲rust設計要求: -
字符串格式化自身是一個【無錯】操作
-
std::fmt::Result僅被用來反映底層輸出流遇到的硬件失敗。
std::fmt::Display與std::fmt::Debug的區別
就功能來說,這兩個trait都差不多。它們之間的差別之處都集中在語義上:
-
std::fmt::Display表示一個類型實例能夠由UTF-8字符串來描述。因爲不是所有類型的實例都是可字符串描述的(只可意會,不可言傳),所以不是所有的類型都需要實現該trait。 -
std::fmt::Debug用於debugging目的,描述某個類型實例的內部數據狀態。所以,理論上,所有的類型都應該實現該trait,以方便隨時按需程序調試。
結束語
這次想和大家分享的內容就是這些。
---------------------
另附上苦瓜小仔的一份思維導圖:
清晰版請參閱:https://www.yuque.com/zhoujiping/programming/pygvaf?inner=sSp4s
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/UtO6FkNAspzZF8HeMWbr7w