结构体字段的声明可以通过之后放置的文字来标记。标签添加由当前包或外部包使用的元信息。让我们首先回首一下 strcut 声明的样子,然后我们将扔出几个用例,深入研究这个标签。
结构体类型(Struct type)
Struct 是一系列字段。每个字段由可选名称和所需类型(源代码)组成:
1 | package main |
T1 域被称为嵌入字段,因为它是用类型声明但没有名称。
字段声明可以在 T2 中指定来自第 3 个字段声明的 f3 和 f4 之类的多个标识符。
语言规范声明每个字段声明后面跟着分号,但正如我们上面所见,它可以省略。如果需要将多个字段声明放入同一行(源代码),分号可能很有用(源代码):
1 | package main |
标签(Tag)
字段声明后面可以跟一个可选的字符串文字(标记),它称为相应字段声明中所有字段的属性(单字段声明可以指定多个标识符)。让我们看看它的实际应用(源代码):
1 | type T struct { |
可以使用原始字符串文字或解释的字符串文字,但下面描述的传统格式需要原始字符串文字。规范中描述了原始字符串文字和解释字符串文字之间的差异。
如果字段声明包含多个标识符,则标记将附加到字段声明的所有字段(如上面的字段 f4 和 f5)。
反射(Reflection)
标签可通过 reflect 包访问,允许运行时反射(源代码):
1 | package main |
设置空标记与完全不使用标记的效果相同(源代码):
1 | type T struct { |
常规格式(Conventional format)
在提交中引入“反射:支持多个包使用结构标记”允许为每个包设置元信息。这提供了简单的命名空间。标签被格式化为键的串联:“值”对。密钥可能是像 json 这样的包的名称。对可以选择用空格分隔 - key1:“ value1” key2: “value2” key3: “value3”。 如果使用传统格式,那么我们可以使用两种 struct tag (StructTag)方法 - Get 或 Lookup。它们允许返回与所需键内部标记相关联的值。
Lookup 函数返回两个值 - 与键关联的值(如果未设置则为空)和 bool,指示是否已找到键(源代码):
1 | type T struct { |
Get 方法只是 Lookup 简单的包封装器,它丢弃了 bool 值(源代码):
1 | func (tag StructTag) Get(key string) string { |
如果标签不是常规模式,则不指定 Get 或 Lookup 的返回值。
即使 tag 是任何字符串值(不管是释义或原始值),只有在双引号(源代码)之间包含值时,Lookup 和 Get 方法才会找到 key 的值:
1 | type T struct { |
可以在解释的字符串值中使用转义双引号(源代码):
1 | type T struct { |
但可读性就要低很多。
结论(Conversion)
将结构体类型转换为其他类型要求底层类型相同,但忽略掉 tag(源代码):
1 | type T1 struct { |
Go 1.8 (提案)中引入了此行为。在 Go 1.7 及更早版本的代码中,可能会抛出编译时错误。
用例(Use cases)
(Un)marshaling
Go 中标签最常见的用途可能是编组。让我们看一下来自 json 包的函数 Marshal 如何使用它(源代码):
1 | import ( |
xml 包也利用了标签 - https://golang.org/pkg/encoding/xml/#MarshalIndent.
ORM
像 GORM 这样的对象关系映射工具,也广泛使用标签 - 例子.
摘要数据(Digesting forms data)
https://godoc.org/github.com/gorilla/schema
其他(Other)
标签的更多潜在用例,如配置管理,结构的默认值,验证,命令行参数描述等(众所周知的结构标记列表)。
go vet
Go 编译器没有强制执行传统的 struct 标签格式,但是 vet 就是这样做的,所以值得使用它,例如作为 CI 管道的一部分。
1 | package main |
…
由于 struct 标签,程序员可以从单一来源中受益。Go 是一种使用语言,所以即使可以使用专用数据结构等其他方式来控制整个过程来解决 JSON/XML 编码,Golang 也能让软件工程师的生活变得更轻松。值得一提的是,标签的长度不受规格的限制。
via: https://medium.com/golangspec/tags-in-golang-3e5db0b8ef3e
作者:Michał Łowicki
译者:gogeof
校对:校对者ID