Tag:
  • tag并不是注释,而是用来对字段进行描述的元数据。尽管它不属于数据成员, 但却是类型的组成部分。
  • 在运行期,可用反射获取标签信息。常被用作格式校验,数据库关系映射等
    Tag是一个字符串,以key、value形式存在,用于标记字段说明,可以配合反射使用,以及Json解析
  • key:不能为空,不能包含、空格、引号、冒号
  • value:使用双引号
type User struct {name string `name:"userName"`age  int    `age:"userAge"`
}

获取结构体Tag内容:

type User struct {name string `name:"userName"`age  int    `age:"userAge"`
}func main() {u := User{}user := reflect.TypeOf(u)field := user.Field(0)fmt.Println(field.Tag.Get("name"))field2 := user.Field(1)fmt.Println(field2.Tag.Get("age"))
}

容易忽视的就是struct tag是类型的组成部分,并非数据注释那么简单

func main() {var a struct { x int    `x`s string `s`}var b struct {x ints string}b = a 	// err:cannot use a type// struct { x int "x"; s string "s" } as type// struct { x int; s string } in assignment
}

自定义类型:

使用关键字type定义用户自定义类型,包括基于现有类型创建,或结构体、有数类型等

func main() {fmt.Println(read, exit)
}type myByte byte	// 从书上看到有这个操作,但是不太理解存在的意义,其实myByte最终指向的还是byte,何必多此一举const (read myByte = iotaexit
)

多个type定义可合并成组,可在函数或代码块内定义局部类型

func main() {type (user struct { // 结构体name stringage  uint}event func(string) // 函数类型)u := user{"itzhuzhu", 24}fmt.Println(u)var f event = func(s string) {fmt.Println(s)}f("haha")
}

但须注意,即便指定了基础类型,也只是表明它们拥有相同的数据结构,两者间不存在任何关系,属完全不同的两种类型。除操作符外,自定义类型不会继承基础类型的任何信息 (包括方法),不能视作别名,不能隐式转换或直接用于比较表达式。

func main() {type data intvar d data = 10var x int = d 	// err: cannot use d (type data) as type int in assignmentprintln(x)println(d == x) // err: invalid operation: d == x (mismatched types data and int)
}

未命名类型:

与有明确标识符的bool、int、string等类型相比array、slice、map、channel等类型与具体的元素类型或长度等属性有关,故称作未命名类型(unnamedtype)。当然,可用type为其提供具体名称以变为命名类型(namedtype)。

具有相同声明的未命名类型被视作同一类型

  • 具有相同基类型的指针
  • 具有相同元素类型和长度的array
  • 具有相同元素类型的slice
  • 具有相同键值类型的map
  • 具有相同数据类型及操作方向的channel
  • 具有相同字段序列(字段名、字段类型、标签以及字段顺序)的struct
  • 具有相同签名(参数和返回值列表,不包括参数名)的function
  • 具有相同方法集(方法名、方法签名,不包括顺序)的interface

函数的参数顺序也属于签名组成部分

func main() {var a func(int, string)var b func(string, int)b = a 	// err: cannot use a (type func(int, string)) as type// func(string, int) in assignmentb("s", 1)
}

未命名类型转换规则:

  • 类型相同
  • 基础类型相同,且其中一个是未命名类型
  • 数据类型相同,将双向channe 1赋值给单向类型,且其中一个为未命名类型
  • 将 nil 赋值给 s1ice、map、channel、pointer、function 以及 interface
  • 实现了目标interface
func main() {type data [2]intvar d data = [2]int{1, 2} //基础类型相同,右值为未命名类型。fmt.Println(d)a := make(chan int, 2)var b chan<- int = a // chan转换为chan<-,其中b为未命名类型。b <- 2
}