使用关键字 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
}