类型
类型决定了值与可在值上应用的操作及方法。类型可以用类型名来标记,也可以用组合类型的类型字面来标记。
组合类型有:数组、结构、指针、函数、接口、切片、映射、通道,都可以用类型字面来构造。
每个类型都有基础类型,对于预定义的类型(布尔型、数值型)、字符串类型、类型字面,基础类型就是它自己。其他情况下,类型T的基础类型就是T在其类型声明中引用的类型的基础类型
方法集
类型可以有相关联的方法集。接口类型的方法集就是它的接口。对于其他类型T,声明了接收者为类型T的所有方法就是它的方法集。而相应的指针类型T的方法集则是声明了接收者为T和T所有方法(也就是包含了类型T的方法集)。对于结构类型,有些特定的规则留在结构类型中说。其他的类型具有空方法集。在方法集中,每个方法都要有一个唯一的非空的方法名。
布尔类型
用预定义的常量true和false来标记布尔真值。预定义的布尔类型是bool,它是一个被定义的类型(defined type.)。
数值类型
预定义的架构无关的数值类型有:
uint8 所有无符号8位整数 (0 to 255)
uint16 所有无符号16位整数(0 to 65535)
uint32 所有无符号32位整数(0 to 4294967295)
uint64 所有无符号64位整数 (0 to 18446744073709551615)
int8 所有有符号8位整数 (-128 to 127)
int16 所有有符号16位整数 (-32768 to 32767)
int32 所有有符号32位整数 (-2147483648 to 2147483647)
int64 所有有符号64位整数 (-9223372036854775808 to 9223372036854775807)
float32 所有IEEE-754 32位浮点数
float64 所有IEEE-754 64位浮点数
complex64 所有具有float32 实数和虚数部分的复数
complex128 所有具有float64 实数和虚数部分的复数
byte uint8的别名
rune int32的别名
还有一些预定义的大小与平台相关的数值类型:
uint either 32 or 64 bits
int same size as uint
uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
为了避免移植性问题,所有的数值类型都是被定义的类型,除了byte和rune,它们是别名。在不同的数值类型混合在一个表达式或语句中时,需要转换类型。
字符串类型
字符串类型是一系列(可能是0个)字节,它一旦创建就不可修改。预定义的类型是string,它是一个被定义的类型。
使用内建的函数len可以获取字符串的长度(字节数),如果字符串是个常量,其长度就是一个编译期常数。可以用下标0~len(s)-1来访问字符串中的指定字节,但不能取其地址,&s[i]是非法的。
数组类型
数组是单一类型(称为元素类型)的指定了数量的一系列元素,元素的数量称为长度且永不为负值。长度是数组类型的一部分,必须被求值为一个值类型为int的的常量表达。可以使用内建的len函数来得到数组长度。其元素可以用下标0~len(a)-1来寻址。数组都是一维的,但可以组合成多维类型。
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))
切片类型
切片是基础数组的一个连续片段的描述符,提供了对该数组中指定数量的一系列元素的访问。
与数组相同的是切片也可以被索引、也有长度、长度也可以用内建函数len来获得。与数组不同的是它在执行中可以改变。其元素可以用下标0~len(a)-1来寻址,但某元素的下标可能比基础数组中相应元素的下标小。
切片一经初始化就与持有其元素的基础数组关联起来。因此它与基础数组以及基础数组上的其他切片一起共享存储。
切片的下层数组可以扩展超过切片的尾部。所以使用容量来度量这个扩展:它是切片的长度加上数组超过切片的那部分长度。可以用内建函数cap来获得切片的容量。
与数组一样,切片总是一维的,但可以通过组合来构造高维度的对象。对于数组的数组,其内层数组总是具有相同的长度,但对于切片的切片,或是切片的数组,其内层的长度可以是不同的。值得一提的是,内层的切片必须单独初始化。
结构类型
结构就是一系列命名的元素(称为字段),每个字段有一个名字和类型。字段的名字可以显式指定也可以隐式指定。非空字段名都必须是唯一的。
// An empty struct.
struct {}
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
对于一个声明了字段类型但没有显式名字的字段,称为嵌入字段。嵌入字段必须指定为类型名T或一个非接口类型名的指针*T,T本身不能是指针类型。未修饰的类型名会作为字段名:
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
T1 // field name is T1
*T2 // field name is T2
P.T3 // field name is T3
*P.T4 // field name is T4
x, y int // field names are x and y
// *T1 // 与字段名T1冲突
//*P.T1 // 未修饰的类型名是T1,与字段名T1冲突
}
对于结构x的嵌入字段的字段或方法f,如果x.f是标记该字段或方法f的合法的选择器,那么称f是被提升的。
被提升的字段的行为就像是结构原本的字段,但是在结构字面中不能用做字段名。
给定一个结构类型S和一个被定义的类型T,结构的方法集中根据以下规则来决定是否包含被提升的方法:
- 如果S包含了嵌入字段T,则S和*S的方法集都包含了接收者为T的被提升方法;*S的方法集还包括了接受者为*T的方法集
- 如果S包含了嵌入字段*T,则S和*S的方法集都包含了接收者为T和*T的被提升方法
字段声明还可以跟着一个字符串字面的标签,它为成为相应字段声明的一个属性。空的标签相当于没有标签。通过反射接口标签才会可见并参与结构的类型标识,否则会被忽略。
struct {
x, y float64 "" // 空标签串相当于没有
name string "任何字符串都可以当标签"
_ [4]byte "这不是结构的字段"
}
// 一个对应TimeStamp协议缓冲区的结构.
// 标签定义了协议缓冲区字段号;
// 它们遵循reflect包中的约定
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}
指针类型
指针类型标记了指向给定类型(称为指针的基类型)的变量的指针。没有初始化的指针的值为nil。
*Point
*[4]int
函数类型
函数类型标记了所有具有相同参数和返回值类型的函数。
参数列表或结果中,名字要么都要有,要么都没有。如果有,一个名字表示一个所指类型的元素(参数或结果),所有的非空名字必须唯一。如果没有,每个类型表示一个所指类型的元素。参数和结果列表总要使用圆括号包围,如果只有一个没名字的结果,则该结果可以省略为不带圆括号的类型名。
最后一个入参可以是一个前缀…的类型。这样的函数称为可变参函数,此参数可代表0或多个参数。
//无返回值无参数
func()
// 一个名字为x的int型参数,有一个int型返回值
func(x int) int
// 一个名字为a的int型参数,一个空的int型参数,一个名字为z的float32型参数,返回值为bool型
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
// 有int型可变参数
func(prefix string, values ...int)
// 有任意类型可变参数,返回一个名为success的bool型值
func(a, b int, z float64, opt ...interface{}) (success bool)
// 返回值有两个,分别是float64型和[]int型指针
func(int, int, float64) (float64, *[]int)
//返回值是个函数类型
func(n int) func(p *T)
接口类型
接口类型指定了一组方法为它的接口。一个类型T的方法集如果是一个接口类型I的接口的超集,则可以存储到类型为I的变量中。而T称为实现了接口I。
一个类型可以在它的方法的子集中实现任意的接口,从而实现多个不同的接口。例如,所有的类型都实现了空接口:
interface {}
接口中的所有方法都必须具有唯一且非空的名字。
接口可以用另一个接口类型的名字来替代方法声明,这称为接口嵌入。此情况下,被嵌入的接口的所有方法都被纳入外层接口。接口的嵌入不可递归。
映射类型
通过某一类型(称键类型)关键字索引一组无序的另一类型(称元素类型)元素,就是映射。
键类型必须要定义比较操作符==和!=,因此,键类型不能是函数、映射或切片。如果键类型是接口类型,必须为动态键值定义这些比较操作符.失败会导致运行时异常。
映射中元素的数量称为长度,可用内建的len函数来获取。长度在运行期可变。可以使用赋值语句来增加元素有索引表达式来获取元素,使用内建delete函数来删除元素。
没有初始化的映射值为nill,不能为它增加元素。
通道类型
通过一个特定元素类型的值的传递,通道提供了并发执行函数之间通信的机制。
chan int
chan <- float64
<- chan byte
其中的 <-
操作符指定了通道的方向——发送还是接收。如果没有指定方向,通道就是双向的。通道可以通过类型转化或是赋值来限制其方向。
<-
操作符是左结合操作符:
chan <- chan int // chan<- (chan int)
chan <- <- chan int // chan<- (<-chan int)
<-chan <-chan int // <-chan (<-chan int)
通道可以带有缓存,如果通道没缓存,则只在收发双方都就绪的情况下通信才能成功。如果通道有缓存,则在缓存未满(发送时)或不空(接收时)的情况下通信会立即成功而不是阻塞。
通道可以用内建函数close来关闭。接收操作符可以使用多值赋值的形式来判断所接收的值是不是在在通道关闭之前发送的。
单个通道在多个goroutines同进发送、接收以及使用内建函数cap、len时都不需要额外的同步操作。通道是先进先出队列。