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) Argument
,逐一遍历arguments
列表中的每一项。但是,当format-spec
内包含.*
时,则会一次迭代遍历两项(下文有详细描述)。- 【顺序访问】不会受【随机访问】·跳跃式寻找
Value Arguments
的影响,因为前者维护了独立的【游标变量】来跟踪迭代位置。[例程1]
- 【顺序访问】不会受【随机访问】·跳跃式寻找
- Value Argument:无要求
- 顺序访问
{positional parameter}
名曰:【索引】(Value) Argument
positional parameter
是integer
值。- 以始于
0
的索引值,随机访问(Value) Argument
。 - Value Argument:无要求
{naming parameter}
名曰:【具名】(Value) Argument
[例程2]naming parameter
是identifier
字符串。- 经由【参数名】,随机访问
(Value) Argument
。 - Value Argument:
- 要么,出现于
Value Arguments
列表的末端;格式:<parameter name>=<value>
。 - 要么,不出现在
Value Arguments
列表中。相反,编译器会- 在当前作用域内,
- 寻找同名绑定变量,
- 使用该绑定变量的值。(馁馁地逆天了)
- 要么,出现于
Formatting argument -> format-spec
以如下五种形式进一步格式化Value Argument
下文中的
[...]
结构表示
- 一对
[
与]
之间的内容是可有可无的。- 而且
[...]
结构是可多层嵌套的。
-
字符串 - 宽度定制
{[<integer | identifier>][:[[[<padding-char>]<align>]<mini-width>][.<max-length>]]}
[例程3]-
padding-char
名曰:“填充”- 缺省。即,【空格】填充。
-
align
名曰:“对齐”<
左 (默认)^
中>
右
若对齐未生效(比如,对
Debug trait
实例),那就- 先·普通格式化
Value argument
, - 再·对结果字符串做·对齐·格式化处理。
-
mini-width
名曰:“最小宽度” -
max-length
名曰:最多显示字符数(从左向右截取)[例程6]- 要么,缺省。即,显示全部字符。
- 要么,数字字面量;
- 要么,
$
后缀【索引值】引用某个Value argument
值 - 要么,
$
后缀【具名变量】引用- 某个
Value argument
值 - 或,当前作用域内,某个绑定变量的值
- 某个
- 要么,
.*
表示当前Formatting argument
同时关联了两个Value argument
[例程7]- 被格式化的值是后一个
Value argument
值 - 而被格式化值前面(或左侧)的
Value argument
代表最多显示字符数
- 被格式化的值是后一个
-
若
mini-width
与max-length
同时指定,并且mini-width
大于max-length
,那么 [例程8]- 先使用
max-length
截断字符串 - 再使用
mini-width
对截断后的字符串有填充与对齐处理
std::fmt::Display::to_string()
成员方法将Value Argument
序列化为字符串。 -
数字 - 宽度定制
{[<integer | identifier>][:[[[<padding-char>]<align>][<sign>][0]<mini-width>][.<precision>]]}
[例程9]-
padding-char
名曰:填充- 缺省。即,【空格】填充。
- 与
sign
后的0
填充符作用不同,padding-char
指定整个数字(含正负号)前后的填充符。padding-char
填充符可以是任意字符。padding-char
的填充优先级低于0
[例程10]
-
align
名曰:对齐<
左^
中>
右 (默认)
若对齐未生效(比如,对
Debug trait
实例),那就- 先·普通格式化
Value argument
, - 再·对结果字符串做·对齐·格式化处理。
-
sign
名曰:正负号- 要么,缺省。即,按需显示
-
号 - 要么,
+
。即,总是显示+/-
号
- 要么,缺省。即,按需显示
-
0
名曰:填充0
数字- 与
padding-char
填充符作用不同,0
表示在【正负号】与【有效数字】之间以数字0
加以填充`。- 填充符号仅能是数字
0
0
填充优先级高于padding-char
[例程11]
- 与
-
mini-width
名曰:最小宽度 -
precision
名曰:精度 [例程14]- 要么,缺省。即,没有限制。
- 要么,数字字面量;
- 要么,
$
后缀【索引值】引用某个Value argument
值 - 要么,
$
后缀【具名变量】引用- 某个
Value argument
值 - 或,当前作用域内,某个绑定变量的值
- 某个
- 要么,
.*
表示当前Formatting argument
同时关联了两个Value argument
[例程15]- 被格式化的值是后一个
Value argument
- 而在被格式值前面(左侧)的
Value argument
代表精度
- 被格式化的值是后一个
-
就数字格式化而言,【正负号】与【进制符】都被计入总宽度内,并挤占了【占位符】的“坑位”。[例程16]
std::fmt::Display::to_string()
成员方法将Value Argument
序列化为字符串。 -
数字 - 进制转换 + 有进制符前缀 + 宽度定制
{[<integer | identifier>][:[[<padding-char>]<align>][<sign>]#[[0]<mini-width>]<numeration>]}
[例程17]-
padding-char
名曰:填充- 缺省。即,【空格】填充。
- 与
#
后0
填充符的作用不同,padding-char
指定整个数字(含·正负号·与·进制符前缀0x
,0o
,0b
·)前后的填充符。padding-char
填充符可以是任意字符。padding-char
的填充优先级低于#0
[例程18]
-
align
名曰:对齐<
左^
中>
右 (默认)
若对齐未生效(比如,对
Debug trait
实例),那就- 先·普通格式化
Value argument
, - 再·对结果字符串做·对齐·格式化处理。
-
sign
名曰:正负号- 要么,缺省。按需显示
-
号 - 要么,
+
。即,总是显示正负号。
- 要么,缺省。按需显示
-
#
名曰:进制换算指令。- 与末尾处的
numeration
参数配套出现。
- 与末尾处的
-
0
名曰:填充0
数字- 与
padding-char
填充符作用不同,#0
表示在【进制符前缀0x
,0o
,0b
】与【有效数字】之间以数字0
加以填充`。- 填充符号仅能是数字
0
#0
填充优先级高于padding-char
[例程19]
- 与
-
mini-width
名曰:最小宽度 -
numeration
名曰:进制符
-
就数字格式化而言,【正负号】与【进制符】都被计入总宽度内,并挤占了【占位符】的“坑位”。[例程22]
任何实现了
Format trait
的【自定义-数据类型】的实例都能被format-spec
指令序列化与格式化。标准库已经为基本数据类型提供了
Format trait
的默认实现。 -
数字 - 进制转换 + 无进制符前缀 + 宽度定制
{[<integer | identifier>][:[[[<padding-char>]<align>][<sign>][[0]<mini-width>]]<numeration>]}
-
padding-char
名曰:填充- 缺省。即,【空格】填充。
- 与
sign
后0
填充符作用不同,padding-char
指定整个数字(含正负号)前后的填充符。padding-char
填充符可以是任意字符。padding-char
的填充优先级低于0
[例程23]
-
align
名曰:对齐<
左^
中>
右 (默认)
若对齐未生效(比如,对
Debug trait
实例),那就- 先·普通格式化
Value argument
, - 再·对结果字符串做·对齐·格式化处理。
-
sign
名曰:正负号- 要么,缺省。按需显示
-
号 - 要么,
+
。即,总是显示正负号
- 要么,缺省。按需显示
-
0
名曰:填充0
数字- 与
padding-char
填充符作用不同,0
表示在【正负号】与【有效数字】之间以数字0
加以填充。- 填充符号仅能是数字
0
0
填充优先级高于padding-char
[例程24]
- 与
-
mini-width
名曰:最小宽度 -
numeration
名曰:进制符
-
就数字格式化而言,【正负号】与【进制符】都被计入总宽度内,并挤占了【占位符】的“坑位”。[例程27]
任何实现了
Format trait
的【自定义-数据类型】的实例都能被format-spec
指令序列化与格式化。标准库已经为基本数据类型提供了
Format trait
的默认实现。 -
有缩进格式化
{[<integer | identifier>][:#?]}
[例程28]- 适用于复合数据结构。比如,
Vec<T>
和HashMap<K, V>
。
- 适用于复合数据结构。比如,
在·格式化字符串·字面量内,转义录入{
与}
字面量
- 转义
{
为{{
- 转义
}
为}}
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
默认实现已经帮助开发者完成了- 对【格式化字符串·字面量】的解析处理
- 和,对
format-spec
指令值的提取工作
开发者仅需调用
std::fmt::Formatter
的成员方法(比如,std::fmt::Formatter::fill(&self)
)就可获取格式化指令的具体值。 -
虽然“抽象”成员方法
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
,以方便随时按需程序调试。
结束语
这次想和大家分享的内容就是这些。
评论区
写评论大神,我收藏与学习之。谢谢!
--
👇
苦瓜小仔: Formating Args 我做过一个导图,顺便分享一下:
https://www.yuque.com/zhoujiping/programming/pygvaf?inner=sSp4s
Formating Args 我做过一个导图,顺便分享一下:
https://www.yuque.com/zhoujiping/programming/pygvaf?inner=sSp4s