类型和值的属性
6 分钟阅读
Properties of types and values 类型和值的属性
Underlying types 底层类型/基本类型
每个类型T
都有一个底层类型。如果T
是预先声明的布尔型、数值型或字符串型之一,或者是一个类型字面量,那么对应的底层类型就是T
本身。否则,T
的底层类型是T
在其声明中所指的类型的底层类型。对于类型参数,则是其类型约束的底层类型,它总是一个接口。
|
|
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
类型,这取决于现存定向信道的方向。
具有核心类型的接口的示例:
|
|
没有核心类型的接口的示例:
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
一些操作(切片表达式、追加和复制)依赖于稍微宽松的核心类型形式,这些核心类型接受字节切片和字符串。具体来说,如果正好有两种类型,[]byte
和string
,它们是接口T
的类型集中所有类型的底层类型,那么T
的核心类型就被称为bytestring
。
具有bytestring
核心类型的接口的例子:
interface{ int } // int (same as ordinary core type) => int (与普通核心类型相同)
interface{ []byte | string } // bytestring
interface{ ~[]byte | myString } // bytestring
注意bytestring
不是一个真正的类型;它不能用来声明变量(是由其他类型组成的)。它的存在只是为了描述一些从字节序列中读取的操作的行为,这些字节序列可能是字节切片或字符串。
Type identity 类型一致性
两种类型要么相同,要么不同。
命名类型总是与任何其他类型不同。否则,如果两个类型的底层类型字面量在结构上是一致的,那么这两个类型就是相同的;也就是说,它们有相同的字面量结构,相应的组成部分拥有一致的类型。详细来说:
- 如果两个数组类型有一致的元素类型和相同的数组长度,那么它们就是一致的。
- 如果两个切片类型有一致的元素类型,那么它们就是一致的。
- 如果两个结构体类型具有相同的字段序列,并且相应的字段具有一致的名称、一致的类型和一致的标签,那么它们就是一致的。来自不同包的不可导出的字段名总是不同的。
- 如果两个指针类型有一致的基本类型,那么它们就是一致的。
- 如果两个函数类型有相同数量的参数和结果值,并且相应的参数和结果类型是相同的,并且两个函数要么都是可变的,要么都不是。参数和结果名称不需要匹配。
- 如果两个接口类型定义了相同的类型集,那么它们就是一致的。
- 如果两个映射类型有一致的键和元素类型,它们就是一致的。
- 如果两个通道类型有一致的元素类型和相同的方向,那么它们是一致的。
- 如果两个实例化的类型的定义类型和所有类型参数都是一致的,那么它们就是一致的。
给出声明:
|
|
这些类型是一致的:
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
是不同的,因为它们是由不同的类型定义所创建的新类型;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的负0被进一步简化为无符号0。请注意,常量值绝不会出现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
一起声明的所有方法的集合。 - 接口类型的方法集是该接口类型集中每个类型的方法集的交集(最终的方法集通常只是接口中声明的方法集)。
进一步的规则,应用于包含嵌入字段的结构体(和结构体指针),会在关于结构体类型的章节中描述。任何其他类型都有一个空的方法集。