假期,我尝试使用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
内包含.*
时,则会一次迭代遍历两项(下文有详细描述)。{positional parameter}
名曰:【索引】(Value) Argument
positional parameter
是integer
值。0
的索引值,随机访问(Value) Argument
。{naming parameter}
名曰:【具名】(Value) Argument
[例程2]Value Arguments
列表的末端;格式:<parameter name>=<value>
。Value Arguments
列表中。相反,编译器会naming parameter
是identifier
字符串。(Value) Argument
。Formatting argument -> format-spec
以如下五种形式进一步格式化Value Argument
下文中的
[...]
结构表
[
与]
之间的内容是可有可无的。[...]
结构是可多层嵌套的。mini-width
与max-length
同时指定,并且mini-width
大于max-length
,那么 [例程8]
std::fmt::Display::to_string()
成员方法将Value Argument
序列化为字符串。std::fmt::Display::to_string()
成员方法将Value Argument
序列化为字符串。Format trait
的【自定义-数据类型】的实例都能被format-spec
指令序列化与格式化。
标准库已经为基本数据类型提供了Format trait
的默认实现。
就数字格式化而言,【正负号】与【进制符】都被计入总宽度内,并挤占了【占位符】的“坑位”。[例程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