类型

Types 类型

原文:https://go.dev/ref/spec#Types

A type determines a set of values together with operations and methods specific to those values. A type may be denoted by a type name, if it has one, which must be followed by type arguments if the type is generic. A type may also be specified using a type literal, which composes a type from existing types.

​ 类型确定了一组值,以及针对这些值的特定操作和方法。如果一个类型有类型名称,可以通过类型名称来表示该类型,如果该类型是泛型的,则必须在类型名称后面跟上类型实参。还可以使用类型字面量来指定类型,该类型由现有类型组成

1
2
3
4
5
6
type      = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeArgs  = "[" TypeList [ "," ] "]" .
TypeList  = Type { "," Type } .
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
            SliceType | MapType | ChannelType .

个人注释

​ 怎么理解“如果该类型是泛型的,则必须在类型名称后面跟上类型实参。”,请看如下示例:

此示例来自Go Tour的泛型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// Index 返回x在s中的索引,若没有找到x,则返回-1
func Index[T comparable](s []T, x T) int {
	for i, v := range s {
		// v和x都是类型T,该类型具有可比较约束,因此我们可以在这里使用 ==。
		if v == x {
			return i
		}
	}
	return -1
}

func main() {
	// 向Index传入整型切片
	si := []int{10, 20, 15, -10}
	fmt.Println(Index(si, 15)) // 2

	// 向Index传入字符串切片
	ss := []string{"foo", "bar", "baz"}
	fmt.Println(Index(ss, "hello")) // -1
}

​ 该示例中,Index 函数中的s和x参数即为类型参数,T类型即为泛型。

​ 搞错了!上面是泛型函数!

​ 以下给出泛型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

type List[T any] struct {
	next  *List[T]
	value T
}

func main() {
	l := List[int]{}
	fmt.Printf("%T,%#v", l, l) // main.List[int],main.List[int]{next:(*main.List[int])(nil), value:0}
}

​ 类型字面量是什么?有哪些类型字面量?

​ 请看ChatGPT是如何回答的:

在Go语言中,类型字面量是用于直接表示和描述类型的语法结构。它可以用来明确指定变量的类型或创建新的类型。

以下是Go语言中常见的类型字面量:

  1. 基本类型字面量:
    • int:整数类型
    • int8:8位整数类型
    • int16:16位整数类型
    • int32:32位整数类型
    • int64:64位整数类型
    • uint:无符号整数类型
    • uint8:8位无符号整数类型
    • uint16:16位无符号整数类型
    • uint32:32位无符号整数类型
    • uint64:64位无符号整数类型
    • uintptr:指针整数类型
    • float32:32位浮点数类型
    • float64:64位浮点数类型
    • complex64:64位复数类型
    • complex128:128位复数类型
    • bool:布尔类型
    • byte:字节类型(等同于uint8
    • rune:Unicode字符类型(等同于int32
    • string:字符串类型
  2. 复合类型字面量:
    • 数组类型字面量:[n]T,其中 n 是数组的长度,T 是数组元素的类型。例如:[5]int 表示包含5个整数元素的数组类型。
    • 结构体类型字面量:struct{},用于定义结构体类型。结构体类型由多个字段组成,每个字段都有自己的类型和名称。例如:struct{ name string; age int } 定义了一个包含 nameage 字段的匿名结构体类型。
    • 指针类型字面量:*T,其中 T 是指针指向的类型。例如:*int 表示整数类型的指针。
    • 函数类型字面量:func(args) returnType,用于定义函数类型。例如:func(int) bool 表示接受一个整数参数并返回布尔值的函数类型。
    • 接口类型字面量:interface{},用于定义接口类型。接口类型定义了一组方法的集合,一个类型只要实现了接口中定义的所有方法,就被视为实现了该接口。
    • 切片类型字面量:[]T,其中 T 是切片元素的类型。例如:[]int 表示整数类型的切片。
    • 映射类型字面量:map[T]U,其中 T 是键的类型,U 是值的类型。例如:map[string]int 表示字符串到整数的映射类型。
    • 通道类型字面量:chan T,其中 T 是通道元素的类型。例如:chan int 表示整数类型的通道。

这些是Go语言中常见的类型字面量示例。通过组合和嵌套这些类型字面量,可以创建更复杂的类型。需要注意的是,还有一些其他的类型字面量,如函数字面量和接口字面量,用于直接定义匿名函数和匿名接口。

The language predeclares certain type names. Others are introduced with type declarations or type parameter lists. Composite types—array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals.

​ 该语言预先声明了某些类型的名称。其他类型是通过类型声明类型参数列表引入的。复合类型:数组、结构体、指针、函数、接口、切片、映射和通道类型 —— 可以用类型字面量来构造。

Predeclared types, defined types, and type parameters are called named types. An alias denotes a named type if the type given in the alias declaration is a named type.

​ 预先声明的类型、已定义的类型和类型参数被称为命名类型。如果别名声明中给出的类型是命名类型,则别名也表示一个(新的)命名类型。

个人注释

​ 在《Go语言精进之路》第24条 第226页,有这么一句话:

已有的类型(比如上面的I、T)被称为underlying类型,而新类型被称为defined类型。新定义的defined类型与原underlying类型是完全不同的类型,那么它们的方法集合上又会有什么关系呢?它们通过

Boolean types 布尔型

A boolean type represents the set of Boolean truth values denoted by the predeclared constants true and false. The predeclared boolean type is bool; it is a defined type.

​ 布尔型表示由预先声明的常量truefalse表示的一组布尔真值。预先声明的布尔类型是bool;它是一个已定义的类型

Numeric types 数值型

An integer, floating-point, or complex type represents the set of integer, floating-point, or complex values, respectively. They are collectively called numeric types. The predeclared architecture-independent numeric types are:

​ 整数类型、浮点类型或复数类型分别表示整数、浮点或复数的值的集合。它们被统称为数值类型。预先声明的与体系结构无关的数值类型有:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32

The value of an n-bit integer is n bits wide and represented using two’s complement arithmetic. =>仍有疑问??

​ 一个n bit整数的值是n bit宽,并用二进制补码运算法(two’s complement arithmetic)表示。

There is also a set of predeclared integer types with implementation-specific sizes:

​ 还有一组预先声明的整数类型,其具体实现的大小因实现而异:

1
2
3
uint     要么32位要么64位
int      与uint大小相同
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value => 一个足够大的无符号整数用于存储指针值的未解释位

To avoid portability issues all numeric types are defined types and thus distinct except byte, which is an alias for uint8, and rune, which is an alias for int32. Explicit conversions are required when different numeric types are mixed in an expression or assignment. For instance, int32 and int are not the same type even though they may have the same size on a particular architecture.

​ 为了避免可移植性问题,所有的数值类型都是已定义的类型,因此除了 byte (它是uint8的别名)和 rune (它是int32的别名)之外,它们是截然不同的。 当不同的数值类型在表达式或赋值中混合使用时,需要进行显式转换。例如,int32和int不是相同类型,尽管它们在一个特定的体系结构上可能具有相同的大小。

String types 字符串型

A string type represents the set of string values. A string value is a (possibly empty) sequence of bytes. The number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string. The predeclared string type is string; it is a defined type.

​ 字符串类型表示字符串值的集合。字符串值是(可能为空的)字节序列。字节数被称为字符串的长度,并且永远不会是负数。字符串是不可变的:一旦创建,就无法改变字符串的内容。预先声明的字符串类型是string;它是一种已定义的类型

The length of a string s can be discovered using the built-in function len. The length is a compile-time constant if the string is a constant. A string’s bytes can be accessed by integer indices 0 through len(s)-1. It is illegal to take the address of such an element; if s[i] is the i‘th byte of a string, &s[i] is invalid.

​ 可以使用内置函数 len 查找字符串 s 的长度。如果字符串是常量,那么长度就是编译时常量。字符串的字节可以通过整数索引0到len(s)-1来访问。取这样一个元素的地址是非法的;如果s[i]是字符串的第i个字节,那么&s[i]是无效的。

个人注释

​ 什么是“&s[i]是无效的”?=> invalid operation

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
	s := "abcd"
	fmt.Println(&s[0]) // invalid operation: cannot take address of s[0] (value of type byte)
	fmt.Println(&s[1]) // invalid operation: cannot take address of s[1] (value of type byte)
}

Array types 数组型

An array is a numbered sequence of elements of a single type, called the element type. The number of elements is called the length of the array and is never negative.

​ 数组是单类型的元素组成的编号序列,称为元素类型。元素的数量被称为数组的长度,并且永远不会是负数。

ArrayType   = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .

The length is part of the array’s type; it must evaluate to a non-negative constant representable by a value of type int. The length of array a can be discovered using the built-in function len. The elements can be addressed by integer indices 0 through len(a)-1. Array types are always one-dimensional but may be composed to form multi-dimensional types.

​ 长度是数组类型的一部分;它必须求值为一个非负常数,该常数可由 int 类型的值表示。数组a的长度可以用内置函数len发现。元素可以通过整数索引0到len(a)-1进行寻址。数组类型总是一维的,但是可以组成多维类型。

1
2
3
4
5
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // same as [2]([2]([2]float64))

个人注释

​ 解释下“[2][2][2]float64 // same as [2]([2]([2]float64))”?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

func main() {
	arr1 := [2][2][2]float64{
		{
			{1, 2},
			{1, 2},
		},
		{
			{1, 2},
			{1, 2},
		},
	}
	arr2 := [2]([2]([2]float64)){
		{
			{1, 2},
			{1, 2},
		},
		{
			{1, 2},
			{1, 2},
		},
	}

	fmt.Printf("arr1=%v\n", arr1) // arr1=[[[1 2] [1 2]] [[1 2] [1 2]]]
	fmt.Printf("arr2=%v\n", arr2) // arr2=[[[1 2] [1 2]] [[1 2] [1 2]]]
}

​ 数组的长度是常量吗?是否可以在声明常量时作为常量的值?=> 是常量,可以作为常量的值!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import "fmt"

var arr = [3]int{1, 2, 3}

const LEN = len(arr)

func main() {
	fmt.Println(LEN) // 3
}

​ 是否可以对数组中的元素进行取地址操作?=> 可以

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import "fmt"

func main() {
	var arr = [3]int{1, 2, 3}
	fmt.Println(&arr[0]) // 0xc000010120
	fmt.Println(&arr[1]) // 0xc000010128
	fmt.Println(&arr[2]) // 0xc000010130
}

An array type T may not have an element of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.

​ 数组类型 T 不能直接或间接地包含类型 T 的元素,或包含类型 T 作为组件的元素,如果这些包含的类型仅为数组或结构类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// invalid array types 非法数组类型
type (
	T1 [10]T1                 // element type of T1 is T1
	T2 [10]struct{ f T2 }     // T2 contains T2 as component of a struct
	T3 [10]T4                 // T3 contains T3 as component of a struct in T4
	T4 struct{ f T3 }         // T4 contains T4 as component of array T3 in a struct
)

// valid array types 合法数组类型
type (
	T5 [10]*T5                // T5 contains T5 as component of a pointer
	T6 [10]func() T6          // T6 contains T6 as component of a function type
	T7 [10]struct{ f []T7 }   // T7 contains T7 as component of a slice in a struct
)

Slice types 切片型

A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array. A slice type denotes the set of all slices of arrays of its element type. The number of elements is called the length of the slice and is never negative. The value of an uninitialized slice is nil.

​ 切片是底层数组的连续段的描述符,并提供对该数组中编号的元素序列的访问。切片类型表示其元素类型的所有数组切片的集合。元素的数量被称为切片的长度,并且永远不会是负数。一个未初始化的切片的值是nil

SliceType = "[" "]" ElementType .

The length of a slice s can be discovered by the built-in function len; unlike with arrays it may change during execution. The elements can be addressed by integer indices 0 through len(s)-1. The slice index of a given element may be less than the index of the same element in the underlying array.

​ 切片s的长度可以通过内置函数len发现;与数组不同,它在运行过程中可能会发生变化。元素可以通过整数索引0到len(s)-1进行寻址。给定元素的切片索引可能小于底层数组中同一元素的索引。

个人注释

​ 切片的长度是常量吗?是否可以在声明常量时作为常量的值?=> 不是常量,不可以作为常量的值!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import "fmt"

var sli = []int{1, 2, 3}

const LEN = len(sli) // len(sli) (value of type int) is not constant

func main() {
	fmt.Println(LEN)
}

​ 解释下“给定元素的切片索引可能小于底层数组中同一元素的索引。”?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func main() {
	arr := []int{0, 1, 2, 3, 4, 5}

	sli := arr[2:]

	fmt.Printf("arr=%v\n", arr)                                 // arr=[0 1 2 3 4 5]
	fmt.Printf("sli=%v\n", sli)                                 // sli=[2 3 4 5]
	fmt.Printf("len(arr)=%d,cap(arr)=%d\n", len(arr), cap(arr)) // len(arr)=6,cap(arr)=6
	fmt.Printf("len(sli)=%d,cap(sli)=%d\n", len(sli), cap(sli)) // len(sli)=4,cap(sli)=4

	for i, v := range arr {
		if v == 2 {
			fmt.Printf("2在arr中的索引是%d\n", i) // 2
		}
	}

	for i, v := range sli {
		if v == 2 {
			fmt.Printf("2在sli中的索引是%d\n", i) // 0
		}
	}

}

A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.

​ 切片一旦被初始化,总是与保存其元素的底层数组相关联。因此,一个切片与它的底层数组和同一数组的其他切片共享存储;相反,不同的数组总是表示不同的存储。

个人注释

​ 解释下“一个切片与它的底层数组和同一数组的其他切片共享存储”?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
	arr := []int{0, 1, 2, 3, 4, 5}

	sli1 := arr[1:]
	sli2 := arr[2:]

	fmt.Printf("arr=%v\n", arr)   // arr=[0 1 2 3 4 5]
	fmt.Printf("sli1=%v\n", sli1) // sli1=[1 2 3 4 5]
	fmt.Printf("sli2=%v\n", sli2) // sli2=[2 3 4 5]

	sli1[1] = 22
	fmt.Printf("arr=%v\n", arr)   // arr=[0 1 22 3 4 5]
	fmt.Printf("sli1=%v\n", sli1) // sli1=[1 22 3 4 5]
	fmt.Printf("sli2=%v\n", sli2) // sli2=[22 3 4 5]

	sli2[1] = 33
	fmt.Printf("arr=%v\n", arr)   // arr=[0 1 22 33 4 5]
	fmt.Printf("sli1=%v\n", sli1) // sli1=[1 22 33 4 5]
	fmt.Printf("sli2=%v\n", sli2) // sli2=[22 33 4 5]
}

The array underlying a slice may extend past the end of the slice. The capacity is a measure of that extent: it is the sum of the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by slicing a new one from the original slice. The capacity of a slice a can be discovered using the built-in function cap(a).

​ 切片的底层数组可以超过切片的末端。容量是对这一范围的衡量:它是切片的长度和切片之外的数组长度之和;可以通过从原始切片切割一个新的切片来创建一个达到这个容量的切片。使用内置函数 cap(a)可以发现切片 a 的容量。

个人注释

​ 解释下“容量是对这一范围的衡量:它是切片的长度和切片之外的数组长度之和”中的“切片之外的数组长度”是什么意思?以及“可以通过从原始切片切割一个新的切片来创建一个达到这个容量的切片”?

=> 可以想象成向右方向还不是(切片中的数组元素的)长度。

=> 只有从最左边的切割原始切片, 产生的切片的容量才和原始切片的容量一致!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import "fmt"

func main() {
	arr := []int{0, 1, 2, 3, 4, 5, 6, 7}

	sli1 := arr[1:4]
	sli2 := arr[1:5]
	sli3 := arr[1:6]

	fmt.Printf("arr=%v,len(arr)=%d,cap(arr)=%d\n", arr, len(arr), cap(arr))       // arr=[0 1 2 3 4 5 6 7],len(arr)=8,cap(arr)=8
	fmt.Printf("sli1=%v,len(sli1)=%d,cap(sli1)=%d\n", sli1, len(sli1), cap(sli1)) // sli1=[1 2 3],len(sli1)=3,cap(sli1)=7
	fmt.Printf("sli2=%v,len(sli2)=%d,cap(sli2)=%d\n", sli2, len(sli2), cap(sli2)) // sli2=[1 2 3 4],len(sli2)=4,cap(sli2)=7
	fmt.Printf("sli3=%v,len(sli3)=%d,cap(sli3)=%d\n", sli3, len(sli3), cap(sli3)) // sli3=[1 2 3 4 5],len(sli3)=5,cap(sli3)=7

	sli10 := sli1[0:1]
	sli20 := sli2[0:1]
	sli30 := sli3[0:1]
	fmt.Printf("sli10=%v,len(sli10)=%d,cap(sli10)=%d\n", sli10, len(sli10), cap(sli10)) // sli10=[1],len(sli10)=1,cap(sli10)=7
	fmt.Printf("sli20=%v,len(sli20)=%d,cap(sli20)=%d\n", sli20, len(sli20), cap(sli20)) // sli20=[1],len(sli20)=1,cap(sli20)=7
	fmt.Printf("sli30=%v,len(sli30)=%d,cap(sli30)=%d\n", sli30, len(sli30), cap(sli30)) // sli30=[1],len(sli30)=1,cap(sli30)=7

	sli11 := sli1[1:2]
	sli21 := sli2[1:2]
	sli31 := sli3[1:2]
	fmt.Printf("sli11=%v,len(sli11)=%d,cap(sli11)=%d\n", sli11, len(sli11), cap(sli11)) // sli11=[2],len(sli11)=1,cap(sli11)=6
	fmt.Printf("sli21=%v,len(sli21)=%d,cap(sli21)=%d\n", sli21, len(sli21), cap(sli21)) // sli21=[2],len(sli21)=1,cap(sli21)=6
	fmt.Printf("sli31=%v,len(sli31)=%d,cap(sli31)=%d\n", sli31, len(sli31), cap(sli31)) // sli31=[2],len(sli31)=1,cap(sli31)=6
	// 从 sli10、sli11、sli20、sli21、sli30、sli31等可以看出,
	// 只有从最左边的切割原始切片,
	// 产生的切片的容量才和原始切片的容量一致!

	sli4 := arr[2:4]
	sli5 := arr[2:5]
	sli6 := arr[2:6]

	fmt.Printf("arr=%v,len(arr)=%d,cap(arr)=%d\n", arr, len(arr), cap(arr))       // arr=[0 1 2 3 4 5 6 7],len(arr)=8,cap(arr)=8
	fmt.Printf("sli4=%v,len(sli4)=%d,cap(sli4)=%d\n", sli4, len(sli4), cap(sli4)) // sli4=[2 3],len(sli4)=2,cap(sli4)=6
	fmt.Printf("sli5=%v,len(sli5)=%d,cap(sli5)=%d\n", sli5, len(sli5), cap(sli5)) // sli5=[2 3 4],len(sli5)=3,cap(sli5)=6
	fmt.Printf("sli6=%v,len(sli6)=%d,cap(sli6)=%d\n", sli6, len(sli6), cap(sli6)) // sli6=[2 3 4 5],len(sli6)=4,cap(sli6)=6

}

A new, initialized slice value for a given element type T may be made using the built-in function make, which takes a slice type and parameters specifying the length and optionally the capacity. A slice created with make always allocates a new, hidden array to which the returned slice value refers. That is, executing

​ 可以使用内置函数make来创建一个给定元素类型T的新的、初始化的切片值,该函数接受一个切片类型和指定长度和可选容量的参数。用make创建的切片总是分配一个新的、隐藏的数组,返回的切片值指向该数组。也就是说,执行

1
make([]T, length, capacity)

produces the same slice as allocating an array and slicing it, so these two expressions are equivalent:

产生的切片与分配一个数组并对其进行切片是一样的,所以这两个表达式是等同的:

1
2
make([]int, 50, 100)
new([100]int)[0:50]

个人注释

​ make和new函数返回的分别是什么类型?是相同的吗?

=> 类型不同!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
	sli1 := make([]int, 6, 6)

	sli2 := new([]int)

	fmt.Printf("make返回的类型是%T\n", sli1) // make返回的类型是[]int
	fmt.Printf("new返回的类型是%T\n", sli2)  // new返回的类型是*[]int

	// arr1 := make([6]int, 6) // invalid argument: cannot make [6]int; type must be slice, map, or channel
	arr2 := new([6]int)
	fmt.Printf("new返回的类型是%T\n", arr2) // new返回的类型是*[6]int
}

​ 那为什么说 make([]int, 50, 100)和new([100]int)[0:50] 等同?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

func main() {
	sli1 := make([]int, 50, 100)

	sli2 := new([100]int)[0:50]

	fmt.Printf("sli1的类型是%T\n", sli1) // sli1的类型是[]int
	fmt.Printf("sli2的类型是%T\n", sli2) // sli2的类型是[]int

}

​ 奇怪了,难道是Go的做了什么特殊处理?TODO

Like arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects. With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.

​ 和数组一样,切片总是一维的,但可以通过组合来构造更高维的对象。对于数组的数组,内部数组在结构上总是相同的长度;但是对于切片的切片(或切片的数组),内部长度可以动态变化。此外,内部切片必须被单独初始化

个人注释

​ 以下例子,应该可以解释“内部切片必须被单独初始化”:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import "fmt"

func main() {
	sli1 := make([][]int, 2, 2)
	// sli1的类型是[][]int,sli1=[[] []],len(sli1)=2,cap(sli1)=2
	fmt.Printf("sli1的类型是%T,sli1=%+v,len(sli1)=%d,cap(sli1)=%d\n", sli1, sli1, len(sli1), cap(sli1))
	//sli1[0][0] = 11 // panic: runtime error: index out of range [0] with length 0
	sli1[1] = []int{1, 2}
	// sli1的类型是[][]int,sli1=[[] [1 2]],len(sli1)=2,cap(sli1)=2
	fmt.Printf("sli1的类型是%T,sli1=%+v,len(sli1)=%d,cap(sli1)=%d\n", sli1, sli1, len(sli1), cap(sli1))
	sli1[1][0] = 11
	// sli1的类型是[][]int,sli1=[[] [11 2]],len(sli1)=2,cap(sli1)=2
	fmt.Printf("sli1的类型是%T,sli1=%+v,len(sli1)=%d,cap(sli1)=%d\n", sli1, sli1, len(sli1), cap(sli1))

	sli2 := *new([][]int)
	// sli2的类型是[][]int,sli2=[],len(sli2)=0,cap(sli2)=0
	fmt.Printf("sli2的类型是%T,sli2=%+v,len(sli2)=%d,cap(sli2)=%d\n", sli2, sli2, len(sli2), cap(sli2))

	//sli2[0][0] = 11 // panic: runtime error: index out of range [0] with length 0
	//sli2[0] = []int{1, 2}     // panic: runtime error: index out of range [0] with length 0                                                                      //
	//sli2[1] = []int{1, 2, 3}     // panic: runtime error: index out of range [1] with length 0                                                                   //

	var sli3 [][]int
	//sli3的类型是[][]int,sli3=[],len(sli3)=0,cap(sli3)=0
	fmt.Printf("sli3的类型是%T,sli3=%+v,len(sli3)=%d,cap(sli3)=%d\n", sli3, sli3, len(sli3), cap(sli3))

	//sli3[0][0] = 11 // panic: runtime error: index out of range [0] with length 0
	//sli3[1] = []int{1, 2, 3} // panic: runtime error: index out of range [1] with length 0

	sli4 := make([][2]int, 2, 2)
	// sli4的类型是[][2]int,sli4=[[0 0] [0 0]],len(sli4)=2,cap(sli4)=2
	fmt.Printf("sli4的类型是%T,sli4=%+v,len(sli4)=%d,cap(sli4)=%d\n", sli4, sli4, len(sli4), cap(sli4))
	sli4[0][0] = 11
	// sli4的类型是[][2]int,sli4=[[11 0] [0 0]],len(sli4)=2,cap(sli4)=2
	fmt.Printf("sli4的类型是%T,sli4=%+v,len(sli4)=%d,cap(sli4)=%d\n", sli4, sli4, len(sli4), cap(sli4))
	//sli4[0] = [3]int{1, 2, 3} // cannot use [3]int{…} (value of type [3]int) as [2]int value in assignment
	sli4[0] = [2]int{111, 222}
	// sli4的类型是[][2]int,sli4=[[111 222] [0 0]],len(sli4)=2,cap(sli4)=2
	fmt.Printf("sli4的类型是%T,sli4=%+v,len(sli4)=%d,cap(sli4)=%d\n", sli4, sli4, len(sli4), cap(sli4))

	sli5 := *new([][2]int)
	// sli5的类型是[][2]int,sli5=[],len(sli5)=0,cap(sli5)=0
	fmt.Printf("sli5的类型是%T,sli5=%+v,len(sli5)=%d,cap(sli5)=%d\n", sli5, sli5, len(sli5), cap(sli5))

	//sli5[0][0] = 11 // panic: runtime error: index out of range [0] with length 0                                                                          // panic: runtime error: index out of range [0] with length 0
	//sli5[0] = [2]int{1, 2} // panic: runtime error: index out of range [0] with length 0

	sli6 := [][2]int{{1, 2}, {2, 3}}
	// sli6的类型是[][2]int,sli6=[[1 2] [2 3]],len(sli6)=2,cap(sli6)=2
	fmt.Printf("sli6的类型是%T,sli6=%+v,len(sli6)=%d,cap(sli6)=%d\n", sli6, sli6, len(sli6), cap(sli6))

	sli6[1] = [2]int{22, 33}
	// sli6的类型是[][2]int,sli6=[[1 2] [22 33]],len(sli6)=2,cap(sli6)=2
	fmt.Printf("sli6的类型是%T,sli6=%+v,len(sli6)=%d,cap(sli6)=%d\n", sli6, sli6, len(sli6), cap(sli6))

	sli6[1] = [...]int{222, 333}
	// sli6的类型是[][2]int,sli6=[[1 2] [222 333]],len(sli6)=2,cap(sli6)=2
	fmt.Printf("sli6的类型是%T,sli6=%+v,len(sli6)=%d,cap(sli6)=%d\n", sli6, sli6, len(sli6), cap(sli6))

	sli7 := [][]int{{1}, {2, 3}, {4, 5, 6}}
	// sli7的类型是[][]int,sli7=[[1] [2 3] [4 5 6]],len(sli7)=3,cap(sli7)=3
	fmt.Printf("sli7的类型是%T,sli7=%+v,len(sli7)=%d,cap(sli7)=%d\n", sli7, sli7, len(sli7), cap(sli7))
	sli7[0] = []int{1, 11, 111}
	// sli7的类型是[][]int,sli7=[[1 11 111] [2 3] [4 5 6]],len(sli7)=3,cap(sli7)=3
	fmt.Printf("sli7的类型是%T,sli7=%+v,len(sli7)=%d,cap(sli7)=%d\n", sli7, sli7, len(sli7), cap(sli7))
	sli7[0][2] = 1111
	// sli7的类型是[][]int,sli7=[[1 11 1111] [2 3] [4 5 6]],len(sli7)=3,cap(sli7)=3
	fmt.Printf("sli7的类型是%T,sli7=%+v,len(sli7)=%d,cap(sli7)=%d\n", sli7, sli7, len(sli7), cap(sli7))
	sli7[0][3] = 11111 // panic: runtime error: index out of range [3] with length 3

}

Struct types 结构体型

A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-blank field names must be unique.

​ 结构体是一系列具有名称和类型的命名元素,称为字段。字段名可以显式指定(IdentifierList)或隐式指定(EmbeddedField)。在结构体内部,非空白字段名必须是唯一的。

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag           = string_lit .
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 一个空结构体
struct {}

// 一个带有6个字段的结构体
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()
}

A field declared with a type but no explicit field name is called an embedded field. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.

​ 使用类型但没有显式字段名声明的字段被称为嵌入字段。嵌入字段必须被指定为一个类型名T或一个指向非接口类型名*T的指针,而且T本身不能是一个指针类型。未限定类型名作为字段名。

1
2
3
4
5
6
7
8
// 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
}

个人注释

​ 什么是“未限定类型名作为字段名”?结构体变量可以在for range中作为被迭代的对象吗?

```go package main import ( "example.com/101/dftype" "fmt" ) type MySt1 struct { dftype.T1 dftype.T2 x, y int } func main() { st1 := MySt1{1, 2, 3, 4} fmt.Printf("st1的类型是%T,st=%+v\n", st1, st1) fmt.Printf("T1字段的值%v\n", st1.T1) //T1字段的值1 fmt.Printf("T2字段的值%v\n", st1.T2) //T2字段的值2 fmt.Printf("x字段的值%v\n", st1.x) //x字段的值3 fmt.Printf("y字段的值%v\n", st1.y) //y字段的值4 //报错: implicit assignment to unexported field x in struct literal of type dftype.MySt1 //报错: implicit assignment to unexported field y in struct literal of type dftype.MySt1 // st2 := dftype.MySt1{1, 2, 3, 4} // fmt.Printf("st2的类型是%T,st2=%+v\n", st2, st2) st2 := dftype.MySt1{T1: 1, T2: 2} fmt.Printf("st2的类型是%T,st2=%+v\n", st2, st2) //st2的类型是dftype.MySt1,st2={T1:1 T2:2 x:0 y:0} fmt.Printf("T1字段的值%v\n", st2.T1) //T1字段的值1 fmt.Printf("T2字段的值%v\n", st2.T2) //T2字段的值2 st3 := dftype.MySt2{1, 2, 3, 4} fmt.Printf("st3的类型是%T,st3=%+v\n", st3, st3) // st3的类型是dftype.MySt2,st3={T1:1 T2:2 X:3 Y:4} fmt.Printf("T1字段的值%v\n", st3.T1) //T1字段的值1 fmt.Printf("T2字段的值%v\n", st3.T2) //T2字段的值2 fmt.Printf("X字段的值%v\n", st3.X) //X字段的值3 fmt.Printf("Y字段的值%v\n", st3.Y) //Y字段的值4 //报错: cannot range over st3 (variable of type dftype.MySt2) //for i, v := range st3 { // fmt.Println(i, ":", v, "\n") //} } ```
```go package dftype type T1 int type T2 int8 type MySt1 struct { T1 T2 x, y int } type MySt2 struct { T1 T2 X, Y int } ```

个人注释

​ 相信以上示例,已经给出了答案:未限定类型名作为字段名,即是将类型名直接作为字段名;结构体变量不能用于 for range语句中作为被迭代对象。

The following declaration is illegal because field names must be unique in a struct type:

​ 下面的声明是非法的,因为字段名在一个结构体类型中必须是唯一的

1
2
3
4
5
struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

​ 个人注释

​ 若嵌入字段是非接口类型名*T的指针,那结构体变量使用%v、%+v或%#v打印时,字段名是什么?输出是怎样的?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

type T1 int
type T2 int8

type MySt1 struct {
	*T1
	T2
}

func main() {
	t := T1(1)
	st := MySt1{&t, 2}
	fmt.Printf("st=%v\n", st)  // st={0xc00001a0a8 2}
	fmt.Printf("st=%+v\n", st) // st={T1:0xc00001a0a8 T2:2}
	fmt.Printf("st=%#v\n", st) // st=main.MySt1{T1:(*main.T1)(0xc00001a0a8), T2:2}
}

A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

​ 如果x.f是表示字段或方法f的合法选择器,那么结构体x中的嵌入字段或方法f被称为(自动)提升(的字段或方法)。

个人注释

​ 请给出提升的字段或方法的示例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import "fmt"

type MySt1 struct {
	Size int
}

// 报错:field and method with the same name Size
//func (mySt1 MySt1) Size() int {
//	return mySt1.Size
//}

func (mySt1 MySt1) Size1() int {
	return mySt1.Size
}

type MySt2 struct {
	MySt1
	Name string
	Age  int
}

func main() {
	st := MySt2{MySt1: MySt1{Size: 20}, Name: "zlongx", Age: 32}
	fmt.Printf("st=%v\n", st)  // st={{20} zlongx 32}
	fmt.Printf("st=%+v\n", st) // st={MySt1:{Size:20} Name:zlongx Age:32}
	fmt.Printf("st=%#v\n", st) // st=main.MySt2{MySt1:main.MySt1{Size:20}, Name:"zlongx", Age:32}

	// 被提升的字段 Size
	fmt.Printf("Size=%d\n", st.Size) // Size=20

	// 被提升的方法 Size1()
	fmt.Printf("Size=%d\n", st.Size1()) // Size=20
}

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

​ 被提升的字段与结构体中的普通字段一样,只是它们不能在结构体的复合字面量中作为字段名使用。

个人注释

​ 解释下“被提升的字段与结构体中的普通字段一样,只是它们不能在结构体的复合字面量中作为字段名使用。”?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

type MySt1 struct {
	Size int
}

// 报错:field and method with the same name Size
//func (mySt1 MySt1) Size() int {
//	return mySt1.Size
//}

func (mySt1 MySt1) Size1() int {
	return mySt1.Size
}

type MySt2 struct {
	MySt1
	Name string
	Age  int
}

func main() {
	// 报错:unknown field Size in struct literal of type MySt2
	// st := MySt2{Size: 20, Name: "zlongx", Age: 32}
	st1 := MySt2{MySt1: MySt1{Size: 20}, Name: "zlongx", Age: 32}
	fmt.Printf("st1=%#v\n", st1) // st1=main.MySt2{MySt1:main.MySt1{Size:20}, Name:"zlongx", Age:32}
	st2 := MySt2{MySt1{Size: 20}, "zlongx", 32}
	fmt.Printf("st2=%#v\n", st2) // st2=main.MySt2{MySt1:main.MySt1{Size:20}, Name:"zlongx", Age:32}

}

​ 在创建结构体 MySt2 的实例时,我们可以使用复合字面量来初始化普通字段 NameAge,但无法直接使用复合字面量来初始化提升字段 Size。我们需要通过指定嵌入字段 MySt1 的普通字段 Size 来初始化它。

​ 即Size这个字段不能直接在结构体字面量的最外围花括号中直接使用。

Given a struct type S and a named type T, promoted methods are included in the method set of the struct as follows:

​ 给定一个结构体类型S和一个命名类型T,提升的方法按以下方式包含在结构体的方法集中:

(aa)If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

​ 如果S包含一个嵌入式字段T,那么S*S的方法集都包括带有接收器T的提升方法,*S的方法集也包括带有接收器*T的提升方法。

(bb) If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

​ 如果S包含一个嵌入式字段*T,那么S*S的方法集都包括带有接收器T*T的提升方法。

​ 个人注释

​ 如下示例中的 a、b、d 可以解释(aa)这一点。

但示例中的c,又该如何解释呢?这应该是go语言编译器的特殊处理或者称为go语法糖!TODO 待找出出处!以及给出编译后汇编代码?

​ 如下示例中的 e、f、g、h 可以解释(bb)这一点。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
package main

import (
	"fmt"
	"reflect"
)

func DumpMethodSet(i interface{}) {
	v := reflect.TypeOf(i)
	elemTyp := v.Elem()

	n := elemTyp.NumMethod()
	if n == 0 {
		fmt.Printf("%s's method set is empty!\n", elemTyp)
	}

	fmt.Printf("%s's method set:\n", elemTyp)
	for j := 0; j < n; j++ {
		fmt.Println("-", elemTyp.Method(j).Name)
	}
	fmt.Println()
}

type T1 struct {
}

func (t T1) Method1A() {
	fmt.Println("Method1A called")
}

func (t *T1) Method1B() {
	fmt.Println("Method1B called")
}

type T2 struct {
}

func (t T2) Method2A() {
	fmt.Println("Method2A called")
}

func (t *T2) Method2B() {
	fmt.Println("Method2B called")
}

type S struct {
	T1
	*T2
}

func main() {
	s := S{T1: T1{}, T2: &T2{}}

	//a. 可以直接调用 接收器为T1 的提升方法 Method1A
	s.Method1A() // Method1A called
	//b. 可以通过指针调用 接收器为T1 的提升方法 Method1A
	(&s).Method1A() // Method1A called

	//c. 可以直接调用 接收器为*T1 的提升方法 Method1B
	s.Method1B() // Method1B called
	//d. 可以通过指针调用 接收器为*T1 的提升方法 Method1B
	(&s).Method1B() // Method1B called

	//e. 可以直接调用 接收器为T2 的提升方法 Method2A
	s.Method2A() // Method2A called
	//f. 可以通过指针调用 接收器为T2 的提升方法 Method2A
	(&s).Method2A() // Method2A called

	//g. 可以直接调用 接收器为*T2 的提升方法 Method2B
	s.Method2B() // Method2B called
	//h. 可以通过指针调用 接收器为*T2 的提升方法 Method2B
	(&s).Method2B() // Method2B called

	var t = s
	var pt = &s
	//main.S's method set:
	//- Method1A
	//- Method2A
	//- Method2B
	DumpMethodSet(&t)
	//*main.S's method set:
	//- Method1A
	//- Method1B
	//- Method2A
	//- Method2B
	DumpMethodSet(&pt)
	fmt.Printf("%T\n", pt)  // *main.S
	fmt.Printf("%T\n", &pt) // **main.S


	var tt S
	var ppt *S
	//main.S's method set:
	//- Method1A
	//- Method2A
	//- Method2B
	DumpMethodSet(&tt)
	//*main.S's method set:
	//- Method1A
	//- Method1B
	//- Method2A
	//- Method2B
	DumpMethodSet(&ppt)
}

A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.

​ 一个字段声明后面可以有一个可选的字符串字面量标签,它成为相应字段声明中所有字段的属性。一个空的标签字符串等同于一个不存在标签。标签通过反射接口可见,并参与结构体的类型标识,但在其他情况下被忽略。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct {
	x, y float64 ""  // an empty tag string is like an absent tag
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// A struct corresponding to a TimeStamp protocol buffer. 
// 一个对应于 TimeStamp 协议缓冲区的结构。
// The tag strings define the protocol buffer field numbers;
// 标签字符串定义协议缓冲区字段编号
// they follow the convention outlined by the reflect package.
// 它们遵循反射包描述的约定。
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

个人注释

​ 请给出怎么通过反射接口给出标签的示例?

A struct type T may not contain a field of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.

​ 结构类型 T 不能包含类型 T 的字段,或者包含类型 T 作为组件的字段,无论直接还是间接,如果这些包含的类型只是数组或结构类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// invalid struct types 非法结构体类型
type (
	T1 struct{ T1 }            // T1 contains a field of T1
	T2 struct{ f [10]T2 }      // T2 contains T2 as component of an array
	T3 struct{ T4 }            // T3 contains T3 as component of an array in struct T4
	T4 struct{ f [10]T3 }      // T4 contains T4 as component of struct T3 in an array
)

// valid struct types 合法结构体类型
type (
	T5 struct{ f *T5 }         // T5 contains T5 as component of a pointer
	T6 struct{ f func() T6 }   // T6 contains T6 as component of a function type
	T7 struct{ f [10][]T7 }    // T7 contains T7 as component of a slice in an array
)

Pointer types 指针型

A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. The value of an uninitialized pointer is nil.

​ 指针类型表示指向给定类型(称为指针的基本类型)变量的所有指针的集合。一个未初始化的指针的值是nil

1
2
PointerType = "*" BaseType .
BaseType    = Type .
1
2
*Point
*[4]int

Function types 函数型

A function type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil.

​ 函数类型表示具有相同参数类型和结果类型的所有函数的集合。一个函数类型的未初始化变量的值是nil

1
2
3
4
5
6
functionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

Within a list of parameters or results, the names (IdentifierList) must either all be present or all be absent. If present, each name stands for one item (parameter or result) of the specified type and all non-blank names in the signature must be unique. If absent, each type stands for one item of that type. Parameter and result lists are always parenthesized except that if there is exactly one unnamed result it may be written as an unparenthesized type.

​ 在参数或结果的列表中,名称(IdentifierList)必须全部存在或全部不存在。如果存在(名称),每个名称代表指定类型的一个项(参数或结果),并且签名中所有非空白的名称必须是唯一的。如果不存在(名称),每个类型代表该类型的一个项。参数和结果列表总是用括号表示,但如果正好仅有一个未命名的结果,则可以写成未括号的类型。

The final incoming parameter in a function signature may have a type prefixed with .... A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.

​ 在函数签名中的最后一个传入参数可以有一个类型前缀 ...。有这样一个参数的函数被称为 variadic (可变参数函数),可以用零个或多个参数来调用该函数。

1
2
3
4
5
6
7
8
func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)

Interface types 接口型

An interface type defines a type set. A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

​ 接口类型定义了一个类型集。接口类型的变量可以存储该接口类型集中的任何类型的值。这样的类型被称为实现了该接口。未初始化的接口类型变量的值是nil

InterfaceType  = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem  = MethodElem | TypeElem .
MethodElem     = MethodName Signature .
MethodName     = identifier .
TypeElem       = TypeTerm { "|" TypeTerm } .
TypeTerm       = Type | UnderlyingType .
UnderlyingType = "~" Type .

An interface type is specified by a list of interface elements. An interface element is either a method or a type element, where a type element is a union of one or more type terms. A type term is either a single type or a single underlying type.

​ 接口类型由接口元素列表指定。接口元素是一个方法或一个类型元素,其中类型元素是一个或多个类型项的联合。类型项可以是一个单一类型,也可以是一个单一的底层类型。

Basic interfaces 基本接口

In its most basic form an interface specifies a (possibly empty) list of methods. The type set defined by such an interface is the set of types which implement all of those methods, and the corresponding method set consists exactly of the methods specified by the interface. Interfaces whose type sets can be defined entirely by a list of methods are called basic interfaces.

​ 在其最基本的形式中,接口指定了一个(可能是空的)方法列表。由这样一个接口定义的类型集是实现了所有这些方法的类型集,而相应的方法集则完全由这个接口指定的方法组成。那些类型集可以完全由一个方法列表来定义的接口被称为基本接口

1
2
3
4
5
6
// A simple File interface.
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

The name of each explicitly specified method must be unique and not blank.

​ 每个显式指定的方法的名称必须是唯一的,不能是空白

1
2
3
4
5
interface {
	String() string
	String() string  // illegal: String not unique
	_(x int)         // illegal: method must have non-blank name
}

More than one type may implement an interface. For instance, if two types S1 and S2 have the method set

​ 多个类型可以实现一个(相同的)接口。例如,如果两个类型S1S2的方法设置为

1
2
3
func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error

(where T stands for either S1 or S2) then the File interface is implemented by both S1 and S2, regardless of what other methods S1 and S2 may have or share.

(其中T代表S1S2),那么File接口就由S1S2实现,而不管S1S2可能有其他方法或共享什么其他方法。

Every type that is a member of the type set of an interface implements that interface. Any given type may implement several distinct interfaces. For instance, all types implement the empty interface which stands for the set of all (non-interface) types:

​ 作为接口类型集成员的每个类型都实现了该接口。任何给定的类型都可以实现几个不同的接口。例如,所有类型都实现空接口 (interface {}),它代表所有(非接口)类型的集合:

1
interface{}

For convenience, the predeclared type any is an alias for the empty interface. [Go 1.18]

​ 为了方便,预先声明的类型any空接口的别名。[Go 1.18]

Similarly, consider this interface specification, which appears within a type declaration to define an interface called Locker:

​ 类似地,考虑这个接口规范,它出现在定义名为 Locker 的接口的类型声明中:

1
2
3
4
type Locker interface {
	Lock()
	Unlock()
}

If S1 and S2 also implement

​ 如果S1S2也实现了

1
2
func (p T) Lock() {}
func (p T) Unlock() {}

they implement the Locker interface as well as the File interface.

他们就实现了Locker接口和File接口。

Embedded interfaces 嵌入接口

In a slightly more general form an interface T may use a (possibly qualified) interface type name E as an interface element. This is called embedding interface E in T [Go 1.14]. The type set of T is the intersection of the type sets defined by T’s explicitly declared methods and the type sets of T’s embedded interfaces. In other words, the type set of T is the set of all types that implement all the explicitly declared methods of T and also all the methods of E [Go 1.18].

​ 接口T可以使用(可能是限定的)接口类型名称E作为接口元素。这就是在 T 中嵌入接口 E [Go 1.14]。T的类型集是由T的显式声明方法定义的类型集和T的嵌入接口的类型集的交集。换句话说,T的类型集是实现T的所有显式声明的方法以及E的所有方法的所有类型的集合。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
type Reader interface {
	Read(p []byte) (n int, err error)
	Close() error
}

type Writer interface {
	Write(p []byte) (n int, err error)
	Close() error
}

// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
	Reader  // includes methods of Reader in ReadWriter's method set
	Writer  // includes methods of Writer in ReadWriter's method set
}

When embedding interfaces, methods with the same names must have identical signatures.

​ 在嵌入接口时,具有相同名称的方法必须具有相同的签名。

1
2
3
4
type ReadCloser interface {
	Reader   // includes methods of Reader in ReadCloser's method set
	Close()  // illegal: signatures of Reader.Close and Close are different
}

General interfaces 通用接口

In their most general form, an interface element may also be an arbitrary type term T, or a term of the form ~T specifying the underlying type T, or a union of terms t1|t2|…|tn [Go 1.18]. Together with method specifications, these elements enable the precise definition of an interface’s type set as follows:

​ 在最通用的形式下,接口元素也可以是一个任意类型项T,或者是一个指定底层类型T~T形式的项,或者是一系列项t1|t2|...|tn的联合 [Go 1.18]。结合方法规范,这些元素能够精确地定义一个接口的类型集,如下所示:

  • The type set of the empty interface is the set of all non-interface types.
  • 空接口的类型集是所有非接口类型的集合
  • The type set of a non-empty interface is the intersection of the type sets of its interface elements.
  • 非空接口的类型集是其接口元素的类型集的交集。
  • The type set of a method specification is the set of all non-interface types whose method sets include that method.
  • 方法规范的类型集是包含该方法的所有非接口类型的集合
  • The type set of a non-interface type term is the set consisting of just that type.
  • 非接口类型项的类型集是仅由该类型组成的集合。
  • The type set of a term of the form ~T is the set of all types whose underlying type is T.
  • 形式为~T的项的类型集是底层类型为T的所有类型的集合。
  • The type set of a union of terms t1|t2|…|tn is the union of the type sets of the terms.
  • 一系列项t1|t2|...|tn的类型集是这些项的类型集的并集。

The quantification “the set of all non-interface types” refers not just to all (non-interface) types declared in the program at hand, but all possible types in all possible programs, and hence is infinite. Similarly, given the set of all non-interface types that implement a particular method, the intersection of the method sets of those types will contain exactly that method, even if all types in the program at hand always pair that method with another method.

​ 量化 “所有非接口类型的集合 “不仅指当前程序中声明的所有(非接口)类型,还指所有可能程序中的所有可能类型,因此是无限的。类似地,给定实现某个特定方法的所有非接口类型的集合,这些类型的方法集的交集将正好包含该方法,即使当前程序中的所有类型总是将该方法与另一个方法配对。

By construction, an interface’s type set never contains an interface type.

​ 根据定义,一个接口的类型集永远不会包含一个接口类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// An interface representing only the type int.
// 仅表示 int 类型的接口
interface {
	int
}

// An interface representing all types with underlying type int.
// 表示底层类型为 int 的所有类型的接口
interface {
	~int
}

// An interface representing all types with underlying type int that implement the String method.
// 表示具有实现 String() 方法 和 底层类型为 int 的所有类型的接口。
interface {
	~int
	String() string
}

// An interface representing an empty type set: there is no type that is both an int and a string.
// 表示空类型集的接口: 没有既是 int 又是 string 的类型。
interface {
	int
	string
}

In a term of the form ~T, the underlying type of T must be itself, and T cannot be an interface.

​ 在形式为~T的项中,T的底层类型必须是它自己,而且T不能是一个接口。

1
2
3
4
5
6
7
type MyInt int

interface {
	~[]byte  // the underlying type of []byte is itself => []byte 的底层类型是其本身
	~MyInt   // illegal: the underlying type of MyInt is not MyInt => 非法的: MyInt的底层类型不是MyInt
	~error   // illegal: error is an interface => 非法的: error 是一个接口
}

Union elements denote unions of type sets:

​ 联合元素表示类型集的并集:

1
2
3
4
5
6
7
// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
// Float接口表示所有的浮点类型(包括底层类型为float32或float64的命名类型)。
type Float interface {
	~float32 | ~float64
}

The type T in a term of the form T or ~T cannot be a type parameter, and the type sets of all non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty). Given a type parameter P:

​ 形式为T~T的项中的类型T不能是类型参数,所有非接口项的类型集必须是成对不相交的(类型集的成对交集必须为空)。给定一个类型参数P:

1
2
3
4
5
6
interface {
	P                // illegal: P is a type parameter => 非法的: P 是一个类型参数
	int | ~P         // illegal: P is a type parameter => 非法的: P 是一个类型参数
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)  => 非法的: ~int 和 MyInt 的类型集是相交的(~int 包括 MyInt)
	float32 | Float  // overlapping type sets but Float is an interface  => 重叠的类型集,更进一步说Float也是一个接口
}

Implementation restriction: A union (with more than one term) cannot contain the predeclared identifier comparable or interfaces that specify methods, or embed comparable or interfaces that specify methods.

​ 实现限制:一个联合(有多个项)不能包含预先声明的标识符中的comparable或指定了方法的接口,或嵌入comparable或指定了方法的接口。

Interfaces that are not basic may only be used as type constraints, or as elements of other interfaces used as constraints. They cannot be the types of values or variables, or components of other, non-interface types.

​ 非基本接口只能作为类型约束使用,或者作为其他接口的元素作为约束使用。它们不能作为值或变量的类型,也不能作为其他非接口类型的组成部分

1
2
3
4
5
6
7
var x Float                     // illegal: Float is not a basic interface => 非法: Float 不是一个基本接口

var x interface{} = Float(nil)  // illegal => 非法

type Floatish struct {
	f Float                 // illegal => 非法
}

An interface type T may not embed a type element that is, contains, or embeds T, directly or indirectly.

​ 接口类型 T 不能嵌入任何递归地包含或嵌入 T 的类型元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// illegal: Bad cannot embed itself => 非法: Bad 不能嵌入自己
type Bad interface {
	Bad
}

// illegal: Bad1 cannot embed itself using Bad2 => 非法: Bad1不能使用 Bad2嵌入自己
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

// illegal: Bad3 cannot embed a union containing Bad3 => Bad3 不能嵌入包含 Bad3的联合
type Bad3 interface {
	~int | ~string | Bad3
}

Implementing an interface 实现一个接口

A type T implements an interface I if

​ 如果类型T实现了接口I,则

​ (a)T is not an interface and is an element of the type set of I; or

T不是接口,并且是I类型集的元素;或者

​ (b) T is an interface and the type set of T is a subset of the type set of I.

T是接口,并且T的类型集是I的类型集的子集。

A value of type T implements an interface if T implements the interface.

​ 如果T实现了一个接口,那么T类型的值就实现了该接口。

个人注释

​ 针对(b)给出示例说明下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
	"fmt"
)

type Reader interface {
	Read()
}

type Writer interface {
	Write(data string)
}

type ReadWriter interface {
	Read()
	Write(data string)
}

type Document struct{}

func (d Document) Read() {
	fmt.Println("Reading document")
}

func (d Document) Write(data string) {
	fmt.Println("Writing \"" + data + "\" to document")
}

func main() {
	var r Reader
	var w Writer
	var rw ReadWriter
	doc := Document{}
	doc.Read()         // Reading document
	doc.Write("hello") //Writing "hello" to document

	r = doc
	w = doc
	rw = doc

	r.Read()         // Reading document
	w.Write("world") // Writing "world" to document
	rw.Read()        // Reading document
	rw.Write("hi")   // Writing "hi" to document
}

Map types 映射型

A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.

​ 映射是一个无序的元素组,由一种类型的元素(称为元素类型)组成,由另一种类型的唯一键集(称为键类型)进行索引。一个未初始化的映射的值是nil

MapType     = "map" "[" KeyType "]" ElementType .
KeyType     = Type .

The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice. If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic.

比较运算符==!=必须为键类型的操作数完全定义;因此键类型不能是函数、映射或切片。如果键类型是接口类型,则必须为动态键值定义这些比较运算符;失败将导致运行时恐慌(run-time panic)

个人注释

​ map的键可以是数组吗?=> 可以

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import "fmt"

func main() {
	var m0 map[[3]int]string
	fmt.Printf("m0=%v\n", m0)  // m0=map[]
	fmt.Printf("m0=%+v\n", m0) // m0=map[]
	fmt.Printf("m0=%#v\n", m0) // m0=map[[3]int]string(nil)
	//m0[[...]int{0, 1, 2}] = "A0" //报错: panic: assignment to entry in nil map
	//m0[[...]int{2, 3, 4}] = "B0" //报错: panic: assignment to entry in nil map

	m1 := map[[3]int]string{}
	fmt.Printf("m1=%v\n", m1)  // m1=map[]
	fmt.Printf("m1=%+v\n", m1) // m1=map[]
	fmt.Printf("m1=%#v\n", m1) // m1=map[[3]int]string{}

	m1[[...]int{0, 1, 2}] = "A1"
	m1[[...]int{2, 3, 4}] = "B1"
	fmt.Printf("m1=%v\n", m1)  // m1=map[[0 1 2]:A1 [2 3 4]:B1]
	fmt.Printf("m1=%+v\n", m1) // m1=map[[0 1 2]:A1 [2 3 4]:B1]
	fmt.Printf("m1=%#v\n", m1) // m1=map[[3]int]string{[3]int{0, 1, 2}:"A1", [3]int{2, 3, 4}:"B1"}

	m2 := map[[3]int]string{[...]int{0, 1, 2}: "A2", [...]int{2, 3, 4}: "B2"}
	fmt.Printf("m2=%v\n", m2)  // m2=map[[0 1 2]:A2 [2 3 4]:B2]
	fmt.Printf("m2=%+v\n", m2) // m2=map[[0 1 2]:A2 [2 3 4]:B2]
	fmt.Printf("m2=%#v\n", m2) // m2=map[[3]int]string{[3]int{0, 1, 2}:"A2", [3]int{2, 3, 4}:"B2"}

	m3 := make(map[[3]int]string)
	fmt.Printf("m3=%v\n", m3)  // m3=map[]
	fmt.Printf("m3=%+v\n", m3) // m3=map[]
	fmt.Printf("m3=%#v\n", m3) // m3=map[[3]int]string{}
	m3[[...]int{0, 1, 2}] = "A3"
	m3[[...]int{2, 3, 4}] = "B3"
	fmt.Printf("m3=%v\n", m3)  // m3=map[[0 1 2]:A3 [2 3 4]:B3]
	fmt.Printf("m3=%+v\n", m3) // m3=map[[0 1 2]:A3 [2 3 4]:B3]
	fmt.Printf("m3=%#v\n", m3) // m3=map[[3]int]string{[3]int{0, 1, 2}:"A3", [3]int{2, 3, 4}:"B3"}

	m4 := *new(map[[3]int]string)
	fmt.Printf("m4=%v\n", m4)  // m4=map[]
	fmt.Printf("m4=%+v\n", m4) // m4=map[]
	fmt.Printf("m4=%#v\n", m4) // m4=map[[3]int]string(nil)
	//m4[[...]int{0, 1, 2}] = "A3" //报错 panic: assignment to entry in nil map
}

​ 若map的键是接口类型,怎么为该动态键值定义比较运算符?TODO

1
2
3
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

The number of map elements is called its length. For a map m, it can be discovered using the built-in function len and may change during execution. Elements may be added during execution using assignments and retrieved with index expressions; they may be removed with the delete and clear built-in function.

​ 映射元素的数量被称为它的长度。对于一个map m来说,它可以用内置函数len来发现,并且在运行过程中可能会改变。在运行过程中可以用赋值添加元素,用索引表达式检索元素;可以用内置函数delete删除元素。

A new, empty map value is made using the built-in function make, which takes the map type and an optional capacity hint as arguments:

​ 使用内置函数 make 创建一个新的空 map 值,它使用 map 类型和一个可选的容量提示作为参数:

1
2
make(map[string]int)
make(map[string]int, 100)

The initial capacity does not bound its size: maps grow to accommodate the number of items stored in them, with the exception of nil maps. A nil map is equivalent to an empty map except that no elements may be added.

​ 初始容量不限制其大小:映射会增长以容纳其中存储的项数,但nil映射除外。nil映射等同于空映射,只是不能添加任何元素

Channel types 通道型

A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil.

​ 通道为并发执行函数提供了一种机制,通过发送接收指定元素类型的值进行通信。未初始化的通道的值是nil

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

The optional <- operator specifies the channel direction, send or receive. If a direction is given, the channel is directional, otherwise it is bidirectional. A channel may be constrained only to send or only to receive by assignment or explicit conversion.

​ 可选的<-操作符指定了通道的方向:发送或接收。如果指定了方向,则该通道是定向的,否则是双向的。通过赋值或显式转换,通道可以被限制为仅发送或仅接收。

1
2
3
chan T          // can be used to send and receive values of type T => 可用于发送或接收类型为 T 的值
chan<- float64  // can only be used to send float64s => 仅用于发送 float64 类型
<-chan int      // can only be used to receive ints => 仅用于接收 int 类型

The <- operator associates with the leftmost chan possible:

<- 操作符尽可能与最左边的 chan 相关联:

1
2
3
4
chan<- chan int    // same as chan<- (chan int) => 与 chan<- (chan int) 相同
chan<- <-chan int  // same as chan<- (<-chan int) =>与 chan<- (<-chan int) 相同
<-chan <-chan int  // same as <-chan (<-chan int) => 与 <-chan (<-chan int) 相同
chan (<-chan int)

A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:

​ 可以使用内置函数 make 创建一个新的、初始化的 channel 值,它以channel 类型和可选的容量作为参数:

1
make(chan int, 100)

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

​ 容量(以元素数量为单位)设置通道中缓冲区的大小。如果容量为零或没有指定,则通道是无缓冲的,只有当发送方和接收方都准备好时,通信才会成功。否则,如果缓冲区不满(可继续发送)或不是空的(可继续接收) ,通道会将数据缓冲起来,并且通信在没有阻塞的情况下成功。一个nil通道不能用于通信。

引用其他书籍

​ 以下摘自《Go语言精进之路》第34条 了解channel的妙用 第348页。 ​ 与无缓冲channel 不同,带缓冲channel 可以通过带有 capacity 参数的内置make 函数创建:c:= make(chan T, capctity) ​ 由于带缓冲channel 的运行时层实现带有缓冲区,因此对带有缓冲channel的发送操作在缓冲区未满、接收操作在缓冲区非空的情况下是异步的(发送或接收无需阻塞等待)。也就是说,对一个带缓冲channel,在缓冲区无数据或有数据但未满的情况下,对其进行发送操作的goroutine不会阻塞;在缓冲区已满的情况下,对其进行发送操作的goroutine会阻塞;在缓冲区为空的情况下,对其进行接收操作的goroutine亦会阻塞。

A channel may be closed with the built-in function close. The multi-valued assignment form of the receive operator reports whether a received value was sent before the channel was closed.

​ 通道可以用内置函数close来关闭。接收操作符的多值赋值形式可以用来判断数据是否在通道关闭之前发送出去。

​ 个人注释

​ 什么是“接收操作符的多值赋值”?

1
value, ok := <-ch

A single channel may be used in send statements, receive operations, and calls to the built-in functions cap and len by any number of goroutines without further synchronization. Channels act as first-in-first-out queues. For example, if one goroutine sends values on a channel and a second goroutine receives them, the values are received in the order sent.

​ 任意数量的goroutines都可以通过发送语句接收操作以及对内置函数caplen的调用,来操作一个通道。通道是一个先入先出的队列。例如,如果一个goroutine在通道上发送数据,第二个goroutine接收这些数据,那么这些数据将按照发送的顺序被接收。

最后修改 March 10, 2024: 更新 (ddf4687)