The Go Programming Language Specification - The Go Programming Language
Types
类型字面量("Type literal")
在 Golang 中,类型字面量("Type literal")指的是一种直接表示某个类型的语法构造。它是用来创建该类型的实例或进行类型转换的一种方式。
例如,对于基本数据类型(如整数、浮点数、字符串等),可以使用类型字面量来声明和初始化变量:
var num int = 10 // 使用 int 类型字面量创建整数变量并赋值
var pi float64 = 3.14 // 使用 float64 类型字面量创建浮点数变量并赋值
var str string = "Hello" // 使用 string 类型字面量创建字符串变量并赋值
此外,类型字面量还可以用于进行类型转换。例如,将一个整数类型转换为浮点数类型:
var num int = 10
var pi float64 = float64(num) // 使用 float64 类型字面量进行类型转换
类型确定了一组值,以及针对这些值的操作和方法。如果类型是泛型类型,则必须在类型名称后加入类型实参。也可以使用类型字面量来指定类型,它由现有的类型组成。
Type = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeArgs = "[" TypeList [ "," ] "]" .
TypeList = Type { "," Type } .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .
语言本身 预先声明 了一些类型名称。其他类型通过 类型声明 或 类型参数列表 引入。 复合类型 -- 数组、结构、指针、函数、接口、切片、映射和通道类型 -- 可以使用类型字面量来构造。
预声明类型、定义类型和类型参数称为命名类型。如果别名声明中给出的类型是命名类型,则别名表示命名类型。
Boolean types
布尔类型表示由预定义常量true和false表示的布尔真值集合。预先声明的布尔类型是 bool;它是一种 定义类型。
Numeric types
整数、浮点或复数类型分别代表整数、浮点或复数值的集合。它们统称为数值类型。预先声明的独立于体系结构的数值类型有
uint8 // 所有无符号 8 位整数(0 至 255)的集合
uint16 // 所有无符号 16 位整数的集合(0 至 65535)
uint32 // 所有无符号 32 位整数的集合(0 至 4294967295)
uint64 // 所有无符号 64 位整数的集合(0 至 18446744073709551615)
int8 // 所有带符号 8 位整数的集合(-128 至 127)
int16 // 所有带符号 16 位整数的集合(-32768 至 32767)
int32 // 所有带符号 32 位整数的集合(-2147483648 至 2147483647)
int64 // 所有带符号 64 位整数的集合(-9223372036854775808 至 9223372036854775807)
float32 // 所有 IEEE-754 32 位浮点数的集合
float64 // 所有 IEEE-754 64 位浮点数的集合
complex64 // 具有 float32 实部和虚部的所有复数的集合
complex128 // 所有具有 float64 实部和虚部的复数的集合
byte // uint8 的别名
rune // int32 的别名
n 位整数的值为 n 位宽,使用 二进制算术 表示。
此外,还有一组预先声明的整数类型,其大小与具体实现有关:
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除外,前者是uint8的别名,后者是int32的别名。在表达式或赋值中混合使用不同数字类型时,需要进行显式转换。例如,"int32 "和 "int "不是同一种类型,即使它们在特定的体系结构上可能具有相同的大小。
String types
字符串类型表示字符串值的集合。字符串值是一个(可能为空)字节序列。字节数称为字符串的长度,且永远不会为负数。字符串是不可变的:一旦创建,就无法更改字符串的内容。预先声明的字符串类型是 string;它是一个 定义类型。
使用内置函数 len,可以得到字符串 s 的长度。如果字符串是常量,长度就是编译时常量。字符串的字节可以通过整数 下标索引 0 至 len(s)-1来访问。获取这种元素的地址是非法的;如果 s[i] 是字符串的第 i 个字节,则 &s[i] 无效。
Array types
数组是由单一类型(称为元素类型)的元素组成的编号的序列。元素的个数称为数组的长度,永远不会是负数。
ArrayType = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .
长度是数组类型的一部分;它的值必须是非负的常量 可表示的int类型的值。数组 a 的长度可以通过内置函数 len发现。元素可以通过 0 到 len(a)-1 的整数 indices 来寻址。数组类型总是一维的,但可以组成多维类型。
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))
如果一个数组类型 T 包含的类型只是数组或结构类型,则该数组类型 T 不能直接或间接地拥有一个 T 类型或包含 T 类型的元素。
// 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
切片是一个下层数组连续片段的描述符,提供对该数组中元素编号序列的访问。切片类型表示其元素类型的所有数组切片的集合。元素的个数称为切片的长度,且永远不会是负数。未初始化的片段的值为 "nil"。
SliceType = "[" "]" ElementType .
切片 s 的长度可以通过内置函数 len得到;与数组不同的是,它可能在执行过程中发生变化。元素可以用整数 indices 0 到 len(s)-1来寻址。给定元素的分片索引可能小于底层数组中相同元素的索引。
切片的底层数组可以延伸到切片的末端。容量是对这一范围的衡量:它是切片的长度与切片之外的数组长度之和;长度达到容量的切片可以通过slicing从原始切片创建一个新的切片。可以使用内置函数 cap(a)得到切片 a 的容量。
可以使用内置函数 make,为给定的元素类型 T 创建一个新的、初始化的切片,该函数接收切片类型和指定长度的参数,以及可选的容量参数。使用 make 创建的切片总是分配一个新的、隐藏的数组,返回的切片值指向该数组。也就是说,执行make([]T, length, capacity)
产生的切片与分配一个数组并对其进行 切分 产生的切片相同,因此这两个表达式是等价的:
make([]int, 50, 100)
new([100]int)[0:50]
与数组一样,切片总是一维的,但可以通过组合来构造更高维的对象。对于数组的数组,内部数组在构造上总是相同长度;但对于切片的切片或切片的数组,内部长度可能会动态变化。此外,内部切片必须单独初始化。
Struct types
结构体是一个由命名元素(称为字段)组成的序列,每个字段都有一个名称和一种类型。字段名可以显式(IdentifierList)或隐式(EmbeddedField)指定。在结构体中,非空白 字段名必须是唯一的。
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag = string_lit .
// 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
}
下面的声明是非法的,因为字段名在结构类型中必须是唯一的:
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
}
如果 x.f 是表示该字段或方法 f 的合法 选择器,则结构体 x 中内嵌的字段或 方法 f 称为 promoted(提升)。
除了不能在结构体的 复合字面量 中用作字段名之外,提升字段的作用与结构体的普通字段相同。
给定一个结构体类型 S 和一个命名类型 T,结构体的方法集中会包含如下推广方法:
- 如果
S包含一个内嵌字段T,则S和*S的方法集 都包含接收器为T的提升方法。*S的方法集也包括接收者为*T的提升方法。 - 如果
S包含内嵌字段*T,则S和*S的方法集都包含接收器为T或*T的提升方法。
字段声明后面可以跟一个可选的字符串tag,它将成为相应字段声明中所有字段的属性。空标签字符串等同于无标签。标签通过反射接口可见,并参与结构体的类型标识,否则将被忽略。
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.
// 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"`
}
如果结构类型 T 包含的类型只是数组或结构类型,则结构类型 T 不能直接或间接地包含类型为 T 的字段或包含 T 作为组成部分的类型的字段。
// 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
指针类型表示所有指向给定类型的变量的指针的集合,称为指针的基本类型。未初始化指针的值为 "nil"。
PointerType = "*" BaseType .
BaseType = Type .
*Point
*[4]int
Function types
函数类型表示参数和结果类型相同的所有函数的集合。函数类型的未初始化变量的值为 "nil"。
FunctionType = "func" Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
在参数或结果列表中,参数名称必须全部存在或全部不存在。如果存在,每个参数名称代表指定类型的一个参数或结果,签名中所有非空白 参数名称必须是唯一的。如果都不存在,则每个参数类型代表一个参数或结果。参数和结果列表总是用括号括起来,但如果只有一个未命名的结果,则可以写成未括起来的类型。
函数签名中的最后一个传入参数可以使用...前缀的类型。带有此类参数的函数称为 可变的,可以使用零个或多个参数来调用该参数。
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
接口类型定义了一个类型集。接口类型的变量可以存储接口类型集中任何类型的值。这种类型被称为实现接口。接口类型的未初始化变量的值为nil。
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem = MethodElem | TypeElem .
MethodElem = MethodName Signature .
MethodName = identifier .
TypeElem = TypeTerm { "|" TypeTerm } .
TypeTerm = Type | UnderlyingType .
UnderlyingType = "~" Type .
接口类型由接口元素列表指定。接口元素既可以是方法,也可以是类型元素,其中类型元素是一个或多个类型的组合。组合类型的一项可以是单一类型,也可以是单一底层类型。
Basic interfaces
接口的最基本形式是指定一系列方法(可能是空)。这种接口定义的类型集是实现所有这些方法的类型集,相应的方法集 完全由接口指定的方法组成。类型集可以完全由方法列表定义的接口称为基本接口。
// A simple File interface.
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
interface {
String() string
String() string // illegal: String not unique
_(x int) // illegal: method must have non-blank name
}
不同类型也可以实现同一个接口。例如,如果两个类型 S1 和 S2 都有方法集
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
(其中 T 代表 S1 或 S2),那么 File 接口就同时由 S1 和 S2 实现,而不管 S1 和 S2 可能拥有或共享哪些其他方法。
作为接口类型集成员的每个类型都会实现该接口。任何给定的类型都可能实现多个不同的接口。例如,所有类型都实现了 空接口,它代表所有(非接口)类型的集合:
interface{}
为方便起见,预先声明的类型 any 是空接口的别名。
同样,请看这个接口规范,它出现在 类型声明 中,定义了一个名为 Locker 的接口:
type Locker interface {
Lock()
Unlock()
}
- 如果
S1和S2也实现了
func (p T) Lock() { … }
func (p T) Unlock() { … }
则它们都实现了Locker接口和File接口。
Embedded interfaces
接口 T 可以使用(可能是限定的)接口类型名称 E 作为接口元素。这被称为在 T 中嵌入 接口 E。T 的类型集是由 T 明确声明的方法定义的类型集和 T 嵌入接口的类型集的交集。换句话说,T的类型集是实现了T的所有明确声明的方法以及E的所有方法的所有类型的集合。
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
}
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
- 空接口的类型集是所有非接口类型的集合。
- 非空接口的类型集是接口元素类型集的交集。
- 方法规范的类型集是其方法集包含该方法的所有非接口类型的集合。
- 非接口类型术语的类型集是仅由该类型组成的集合。
- 形式为
~T的的类型集是其基础类型为T的所有类型的集合。 t1|t2|...|tn的联合类型集是类型集t1、t2、...、tn的联合。
所有非接口类型的集合 "这个定量不仅指当前程序中声明的所有(非接口)类型,而且指所有可能程序中的所有可能类型,因此是无限的。同样,给定实现了某个特定方法的所有非接口类型的集合,这些类型的方法集的交集将恰好包含该方法,即使当前程序中的所有类型总是将该方法与另一个方法配对。 构造过程保证了接口的类型集永远不会包含接口类型。
// An interface representing only the type int.
interface {
int
}
// An interface representing all types with underlying type int.
interface {
~int
}
// An interface representing all types with underlying type int that implement the String method.
interface {
~int
String() string
}
// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
int
string
}
如果用 ~T的形式来表示类型, T必须是底层类型, 且不能是接口类型.
type MyInt int
interface {
~[]byte // the underlying type of []byte is itself
~MyInt // illegal: the underlying type of MyInt is not MyInt
~error // illegal: error is an interface
}
联合体元素表示类型集合的联合
// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
~float32 | ~float64
}
形式为 T 或 ~T 的类型 T 不能是类型参数,所有非接口的类型集必须正交(类型集的正交必须为空)。给定一个类型参数 P:
interface {
P // illegal: P is a type parameter
int | ~P // illegal: P is a type parameter
~int | MyInt // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
float32 | Float // overlapping type sets but Float is an interface
}
实现限制: 联合体(包含一个以上的项)不能包含预先声明的标识符 可比较的或指定方法的接口,或嵌入可比较的或指定方法的接口。
非基本接口只能用作类型约束,或用作其他接口的约束元素。它们不能作为值或变量的类型,也不能作为其他非接口类型的组成部分。
var x Float // illegal: Float is not a basic interface
var x interface{} = Float(nil) // illegal
type Floatish struct {
f Float // illegal
}
接口类型 T 不能直接或间接地嵌入属于、包含或嵌入 T 的类型中或者接口中。
// illegal: Bad may not embed itself
type Bad interface {
Bad
}
// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
~int | ~string | Bad3
}
// illegal: Bad4 may not embed an array containing Bad4 as element type
type Bad4 interface {
[10]Bad4
}
Implementing an interface
如果满足下述条件,我们说类型 T 实现了接口 I:
T不是一个接口,并且是I类型集的一个元素;T是一个接口,并且T的类型集是I类型集的子集。 如果类型T实现了一个接口,那么T类型的值就实现了该接口。
Map types
映射(map)是由某种类型(称为元素类型)的元素组成的无序集合,并由另一种类型(称为键类型)的一组唯一键进行索引。未初始化的 map 的值为 "nil"。
MapType = "map" "[" KeyType "]" ElementType .
KeyType = Type .
键类型必须支持===和!=比较运算符 ;因此键类型不能是函数、映射或片段。如果键类型是接口类型,则必须为动态键值定义这些比较运算符;否则将导致运行时恐慌(panic)。
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
map元素的数量称为map的长度。对于map m,可以使用内置函数 len得到m的长度,并可能在执行过程中改变。在执行过程中,可以使用assignments添加元素,使用index expressions检索元素;使用内置函数delete删除元素。
使用内置函数 make可以创建一个新的、空的map,该函数将map的类型和可选的容量提示作为参数:
make(map[string]int)
make(map[string]int, 100)
初始容量并不限制map的大小:映射会随着存储项数量的增加而增长,但 "nil" map除外。"nil" map等同于空map,只是不能添加任何元素。
Channel types
通道通过发送 和接收 指定元素类型的值,为并发执行的函数 提供通信机制。未初始化通道的值为 "nil"。
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
可选的<-操作符指定通道的方向、发送或接收。如果给出了方向,通道就是定向,否则就是双向。通过 赋值 或显式 转换,一个通道可能只限于发送或接收。
chan T // can be used to send and receive values of type T
chan<- float64 // can only be used to send float64s
<-chan int // can only be used to receive ints
操作符"<-"与最左边的 "chan "相关联:
chan<- chan int // same as chan<- (chan int)
chan<- <-chan int // same as chan<- (<-chan int)
<-chan <-chan int // same as <-chan (<-chan int)
chan (<-chan int)
新的初始化通道可以使用内置函数 make创建,该函数的参数包括通道类型参数和可选的容量参数:
make(chan int, 100)
容量(以元素个数为单位)设定了信道缓冲区的大小。如果容量为零或不存在,通道就是无缓冲的,只有当发送方和接收方都准备好时,通信才会成功。否则,通道是缓冲通道,如果缓冲区未满(发送)或未空(接收),则通信成功,不会阻塞。nil "通道永远不会准备好进行通信。
通道可以使用内置函数 close关闭。接收操作符](go.dev/ref/spec#Re…) 的多值赋值形式会报告通道关闭前是否发送了接收值。
单个通道可用于发送、接收以及任意多个goroutine对内置函数cap和len的调用,而无需更多的同步机制。通道就像一个先进先出的队列。例如,如果一个goroutine在通道上发送值,而第二个goroutine接收这些值,那么接收值的顺序就是发送的顺序。
Properties of types and values
Underlying types
每个类型 T 都有一个底层类型: 如果 T 是预先声明的布尔、数字或字符串类型之一,或者是一个字面类型,那么相应的底层类型就是 T 本身。否则,T的底层类型就是T在声明中指向的类型的底层类型。对于类型参数来说,其类型约束的底层类型总是接口。
type (
A1 = string
A2 = A1
)
type (
B1 string
B2 B1
B3 []B1
B4 B3
)
func f[P any](x P) { … }
string、A1、A2、B1和 B2的底层类型是 string。[]B1、B3 和 B4 的底层类型是 []B1。P 的基础类型是 interface{}。
Core types
每个非接口类型T都有一个核心类型,它与T的底层类型相同。
如果满足以下条件之一,接口 T 就有了核心类型:
其他接口没有核心类型。
接口的核心类型取决于所满足的条件:
U类型;或- 如果
T只包含双向通道,则是chan E类型,或者根据存在的定向通道的方向,是chan<- E或<-chan E类型。
具有核心类型的接口示例:
type Celsius float32
type Kelvin float32
interface{ int } // int
interface{ Celsius|Kelvin } // float32
interface{ ~chan int } // chan int
interface{ ~chan int|~chan<- int } // chan<- int
interface{ ~[]*data; String() string } // []*data
没有核心类型的接口示例:
interface{} // no single underlying type
interface{ Celsius|float64 } // no single underlying type
interface{ chan int | chan<- string } // channels have different element types
interface{ <-chan int | chan<- int } // directional channels have different directions
某些操作(切片表达式、append 和 copy)依赖于一种略微松散的核心类型形式,它接受字节切片和字符串。具体地说,如果接口 T 的类型集中所有类型的底层类型正好是两个类型,即 []byte 和 string,那么 T 的核心类型就称为 bytestring。
具有 bytestring 核心类型的接口示例:
interface{ int } // int (same as ordinary core type)
interface{ []byte | string } // bytestring
interface{ ~[]byte | myString } // bytestring
请注意,bytestring 并不是一个真正的类型;它不能用来声明变量或组成其他类型。它的存在只是为了描述从字节序列(可以是字节片或字符串)读取的某些操作的行为。
Type identity
两个类型要么相同,要么不同。
命名类型](go.dev/ref/spec#Ty…) 总是不同于其他任何类型。否则,如果两个类型的底层 类型字面在结构上等同,即它们具有相同的字面结构,相应的组件具有相同的类型,那么这两个类型就是相同的。具体来说
- 如果两个数组具有相同的元素类型和相同的数组长度,则它们是相同的。
- 如果两个切片类型的元素类型相同,则它们是相同的。
- 如果两个结构体类型具有相同的字段序列,并且对应的字段具有相同的名称、相同的类型和相同的标记,则这两个结构体类型是相同的。来自不同软件包的非导出 字段名总是不同的。
- 如果两个指针类型的基本类型相同,它们就是相同的。
- 如果两个函数类型的参数和结果值数目相同,相应的参数和结果类型也相同,并且两个函数都是可变函数或两个函数都不是可变函数,那么这两个函数类型就是相同的。参数和结果名称不需要匹配。
- 如果两个接口类型定义了相同的类型集,则它们是相同的。
- 如果两个map类型的键和元素类型相同,则它们是相同的。
- 如果两个通道类型具有相同的元素类型和相同的方向,则它们完全相同。
- 如果两个实例化 类型定义的类型和所有类型参数都相同,则它们完全相同。 给定以下声明:
type (
A0 = []string
A1 = A0
A2 = struct{ a, b int }
A3 = int
A4 = func(A3, float64) *A0
A5 = func(x int, _ float64) *[]string
B0 A0
B1 []string
B2 struct{ a, b int }
B3 struct{ a, c int }
B4 func(int, float64) *B0
B5 func(x int, y float64) *A1
C0 = B0
D0[P1, P2 any] struct{ x P1; y P2 }
E0 = D0[int, string]
)
这些类型是相同的:
A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5
B0 and C0
D0[int, string] and E0
[]int and []int
struct{ a, b *B5 } and struct{ a, b *B5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
B0 和 B1 之所以不同,是因为它们是由不同的 [类型定义] (go.dev/ref/spec#Ty…) 创建的新类型;func(int,float64) *B0 和 func(x int, y float64) *[]string 之所以不同,是因为 B0 不同于 []string;P1 和 P2 之所以不同,是因为它们是不同的类型参数。D0[int,string]和struct{ x int; y string }不同,因为前者是实例化定义的类型,而后者是类型文字(但它们仍然是可赋值)。
Assignability
如果符合以下条件之一,则V类型的值x可*赋值给T类型的变量(x可赋值给T):
V和T相同。V和T具有相同的底层类型,但不是类型参数,且V或T中至少有一个不是命名类型。V和T是具有相同元素类型的通道类型,V是双向通道,且V或T中至少有一个不是已命名类型。T是接口类型,但不是类型参数,且x实现T。x是预先声明的标识符nil,T是指针、函数、片段、映射、通道或接口类型,但不是类型参数。x是一个未标识具体类型的常量 可表示T类型的值。
此外,如果 x 的类型 V 或 T 是类型参数,那么在符合以下条件之一的情况下, x 可以赋值给类型为 T 的变量:
x是预先声明的标识符nil,T是一个类型参数,并且x可以赋值给T类型集合中的每个类型。V不是命名类型,T是一个类型参数,且x可分配给T的类型集中的每个类型。V是一个类型参数,T不是一个命名类型,并且V的类型集中的每个类型的值都可以赋值给T。
Representability
一个常量 x可由类型T的值表示,其中T不是类型参数,如果下列条件之一适用:
x在T确定的值集中。T是浮点类型,且x可以舍入到T的精度而不会溢出。四舍五入使用 IEEE 754 四舍五入规则,但 IEEE 负零进一步简化为无符号零。请注意,常量值绝不会产生 IEEE 负零、NaN 或无穷大。T是一种复数类型,而x的 分量real(x)和imag(x)可用T的分量类型(float32或float64)的值表示。
如果 T 是一个类型参数,如果 x 可以用 T 类型集合中的每个类型的值来表示,则 x 可以用 T 类型的值来表示。
x T x is representable by a value of T because
'a' byte 97 is in the set of byte values
97 rune rune is an alias for int32, and 97 is in the set of 32-bit integers
"foo" string "foo" is in the set of string values
1024 int16 1024 is in the set of 16-bit integers
42.0 byte 42 is in the set of unsigned 8-bit integers
1e10 uint64 10000000000 is in the set of unsigned 64-bit integers
2.718281828459045 float32 2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
-1e-1000 float64 -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
0i int 0 is an integer value
(42 + 0i) float32 42.0 (with zero imaginary part) is in the set of float32 values
x T x is not representable by a value of T because
0 bool 0 is not in the set of boolean values
'a' string 'a' is a rune, it is not in the set of string values
1024 byte 1024 is not in the set of unsigned 8-bit integers
-1 uint16 -1 is not in the set of unsigned 16-bit integers
1.1 int 1.1 is not an integer value
42i float32 (0 + 42i) is not in the set of float32 values
1e1000 float64 1e1000 overflows to IEEE +Inf after rounding
Method sets
一个类型的方法集决定了以该类型作为操作数可以调用的方法。每个类型都有一个与之相关的方法集(可能为空):
- 定义类型
T的方法集由所有以接收器类型T声明的方法组成。 - 定义类型
T的指针(其中T既不是指针也不是接口)的方法集是以接收器*T或T声明的所有方法的集合。 - 接口类型](go.dev/ref/spec#In…) 的方法集是接口的类型集 中每个类型的方法集的交集(得到的方法集通常只是接口中已声明的方法集)。
进一步的规则适用于包含嵌入字段的结构体(和结构体指针),详见结构体类型 部分。任何其他类型的方法集都是空的。