当前位置 : 主页 > 编程语言 > java >

[Rust笔记] format! 宏使用心得-汇总

来源:互联网 收集:自由互联 发布时间:2022-06-23
假期,我尝试使用​​rust​​​做一款命令行工具,来磨砺自己的​​rust​​​技术熟练度。起初,面对各式各样的字符串格式化功能点,我傻乎乎地尝试自己造轮子。但,实在是遇

假期,我尝试使用​​rust​​​做一款命令行工具,来磨砺自己的​​rust​​​技术熟练度。起初,面对各式各样的字符串格式化功能点,我傻乎乎地尝试自己造轮子。但,实在是遇到了太多技术难点(可难死我了)。后来,通过在论坛发贴请教,我才了解到【标准库 - ​​format!​​​语法扩展】已经​​90%​​​地满足了我的需求。至于,剩余​​10%​​​的功能,可通过实现不同的​​format trait​​来深度定制-达成。

于是,我修改了假期目标为:

  • 重新复习​​format!​​宏相关知识点。相比于之前(真是学过​​N​​遍了),我要提高对这块知识点的重视程度。在复习过程,做些归纳总结和条理清晰的笔记。
  • 基于【标准库 -​​format!​​语法扩展】与【实现​​format trait​​深度定制】的手段,来做一款​​rust​​命令行工具。而不是,自己重新造轮子。

下面即是我对第一个目标的达成成果(除了丰富的教条总结归纳,还有​​30​​​个例程) --- 掌握​​rust​​,先做“教条侠”。第二个目标还在进行中...

依赖关系宏观架构图

[Rust笔记] format! 宏使用心得-汇总_进制

宏调用格式

​​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​​序列化为字符串。


  • [Rust笔记] format! 宏使用心得-汇总_进制_02
  • ​​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​​序列化为字符串。


    • [Rust笔记] format! 宏使用心得-汇总_进制_03
    • ​​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​​的默认实现。


    • [Rust笔记] format! 宏使用心得-汇总_后缀_04
    • ​​padding-char​​名曰:填充
    • ​​align​​名曰:对齐

    若对齐未生效(比如,对​​Debug trait​​实例),那就


    • ​​sign​​名曰:正负号
    • ​​#​​名曰:进制换算指令。
    • ​​0​​​名曰:填充​​0​​数字
    • ​​mini-width​​名曰:最小宽度
    • ​​numeration​​​名曰:进制符
    • [Rust笔记] format! 宏使用心得-汇总_后缀_05
    • ​​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​​,
  • 再·对结果字符串做·对齐·格式化处理。
    • 数字 - 进制转换 +无进制符前缀 + 宽度定制
      [Rust笔记] format! 宏使用心得-汇总_进制_06

    就数字格式化而言,【正负号】与【进制符】都被计入总宽度内,并挤占了【占位符】的“坑位”。[例程27]

    任何实现了​​Format trait​​​的【自定义-数据类型】的实例都能被​​format-spec​​指令序列化与格式化。

    标准库已经为基本数据类型提供了​​Format trait​​的默认实现。


    • ​​padding-char​​名曰:填充
    • ​​align​​名曰:对齐

    若对齐未生效(比如,对​​Debug trait​​实例),那就


    • ​​sign​​名曰:正负号
    • ​​0​​​名曰:填充​​0​​数字
    • ​​mini-width​​名曰:最小宽度
    • ​​numeration​​​名曰:进制符
    • [Rust笔记] format! 宏使用心得-汇总_后缀_07
    • ​​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​​,以方便随时按需程序调试。

    结束语

    这次想和大家分享的内容就是这些。

    ---------------------

    另附上苦瓜小仔的一份思维导图:

    [Rust笔记] format! 宏使用心得-汇总_字符串_08



    【文章转自高防服务器 http://www.558idc.com 复制请保留原URL】
    网友评论