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

type flags byte
  
const ( 
   read flags = 1 << iota
   write
   exec
) 
 
func main() { 
   f := read | exec
   fmt.Printf("%b\n", f)     // 输出二进制标记位 
}

和 var、const 类似,多个 type 定义可合并成组,可在函数或代码块内定义局部类型。

type ( // 组 
	user struct { // 结构体 
	   name string
	   age uint8
	} 
 
	event func(string)bool // 函数类型 
)

即便指定了基础类型,也只表明它们有相同底层数据结构,两者间不存在任何关系,属完全不同的两种类型。

操作符外,自定义类型不会继承基础类型的其他信息(包括方法)。

不能视作别名,不能隐式转换,不能直接用于比较表达式

未命名类型

与有明确标识符的 bool、int、string 等类型相比,数组、切片、字典、通道等类型与具体元素类型或长度等属性有关,故称作未命名类型(unnamed type)。当然,可用 type 为其提供具体名称,将其改变为命名类型(named type)。

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

  • 具有相同基类型的指针。
  • 具有相同元素类型和长度的数组(array)。
  • 具有相同元素类型的切片(slice)。
  • 具有相同键值类型的字典(map)。
  • 具有相同数据类型及操作方向的通道(channel)。
  • 具有相同字段序列(字段名、字段类型、标签,以及字段顺序)的结构体(struct)。
  • 具有相同签名(参数和返回值列表,不包括参数名)的函数(func)。
  • 具有相同方法集(方法名、方法签名,不包括顺序)的接口(interface)。

容易被忽视的是 struct tag,它也属于类型组成部分,而不仅仅是元数据描述。

var a struct { // 匿名结构类型 
   x int    `x` 
   s string `s` 
} 
 
var b struct { 
   x int
   s string
} 
 
b = a      // 错误
// cannot use a type struct{x int"x";s string"s" } as type struct{x int;s string}in assignment

同样,函数的参数顺序也属签名组成部分。

未命名类型转换规则:

  • 所属类型相同。
  • 基础类型相同,且其中一个是未命名类型。
  • 数据类型相同,将双向通道赋值给单向通道,且其中一个为未命名类型。
  • 将默认值 nil 赋值给切片、字典、通道、指针、函数或接口。
  • 对象实现了目标接口。
func main() { 
   type data[2] int
   var d data = [2]int{ 1, 2 }  // 基础类型相同,右值为未命名类型
   fmt.Println(d) 
 
   a := make(chan int, 2) 
   var b chan<-int = a // 双向通道转换为单向通道,其中b为未命名类型 
   b<-2
}