go 语言规范

The Go Programming Language Specification

介绍(Introduction)
Go 是一个适合系统编程的通用语言。它具有强类型、垃圾回收和支持并发编程等特性。Go 程序由包(package)组成,包能有效地管理依赖。当前使用传统的编译、链接方式来生成可执行文件。

源文件(Source code representation)
源代码是 UTF-8 编码的文本文件,Go 不对源文件进行正式化(canonicalized)。

字符 (Characters)
下列 terms 用于表示 Unicode 字符类 (character classes):

newline = /* the Unicode code point U+000A / .
unicode_char = /
an arbitrary Unicode code point except newline / .
unicode_letter = /
a Unicode code point classified as “Letter” / .
unicode_digit = /
a Unicode code point classified as “Decimal Digit” */ .
unicode_char 是除了换行符外的其它字符,范围比 unicode_letter 大。

字母和数字(Letters and digits)
下划线被当做一个字母:

letter = unicode_letter | “_” .
decimal_digit = “0” … “9” .
octal_digit = “0” … “7” .
hex_digit = “0” … “9” | “A” … “F” | “a” … “f” .
下划线被当做字母;不支持二进制数字;

词法元素 (Lexical elements)
注释 (Comments)
两种注释方式,不能嵌套使用:

行注释:从字符序列 // 开始,直到行尾;行注释被当做一个换行;
通用注释:从字符序列 /* 开始,直到 */ 结束;
包含换行的通用注释等效为一个换行符,否则等效为一个空格字符;

Tokens
token 形成了 Go 语言的词汇,编译器读取源文件,去掉注释,用空白字符将源文件划分为一系列 Tokens,然后解释各 token 的含义如表达式、声明、语句等;

有四种类型的 token:标识符(identifiers)、关键字(keywords)、运算符和分隔符(operators and delimiters)和字面量(literals)。

各种空白字符(空格、\f、\t、\r、\n)在编译时会被忽略,但不会忽略用于分隔 token 的空格(否则多个 token 会错误地被当做一个更长的 token);

编译器可能在换行符后或文件结尾插入一个分号。

将源文件划分 token 时,总使用下一个最长的 token;

由于编译器会忽略空白字符,所以 Go 源文件的格式是自由的。

分号(Semicolons)
Go 的正式语法使用分号来终止 (terminate) 语句, 在下面情况下编译器会自动在行末尾添加分号:

当前行非空且最后一个 token 是:

标识符;
整型、浮点型、虚数、rune、字符串 字面量;
break, continue, fallthrough, 或 return 关键字;
++, --, ), ], 或 } 运算符或分隔符;
) 或 } 前的分号(目的是可以让一个复杂的语句占据一行);

由于编译器会自动在符合上面条件的 token 后自动插入分号,所有绝大部分情况下 Go 的源码文件中不需要分号。

标识符(Identifiers)
标识符用于语命名程序中的实体 (entities),例如变量和类型等。

标识符是字母和数字的序列,第一个字符必须是字母(字母包含下划线_):

identifier = letter { letter | unicode_digit } .
Go 预定义了一些标识符(标识符和关键字是有区别的,后者不能被重新绑定)。

Types:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr

Constants:
true false iota

Zero value:
nil

Functions:
append cap close complex copy delete imag len
make new panic print println real recover
关键字(Keywords)
关键字是保留的标识符,它们不能在程序中重新定义(共 25 个关键字):

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
有些预定义的表示类型和常量值标识符,虽然不是关键字,但是也不建议重复定义:

运算符和标点(Operators and punctuatio)
下列字符序列被当做运算符、分隔符或标点:

  • & += &= && == != ( )
  • | -= |= || < <= [ ]
  • ^ *= ^= <- > >= { }
    / << /= <<= ++ = := , ;
    % >> %= >>= – ! … . :
    &^ &^=
    整型字面量(Integer literals)
    整形字面量由数字序列组成,代表了整形常量。数字序列默认为十进制,可以在前面添加可选前缀来改变进制:0 表示八进制、0x 或 0X 表示十六进制。

int_lit = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( “1” … “9” ) { decimal_digit } .
octal_lit = “0” { octal_digit } .
hex_lit = “0” ( “x” | “X” ) hex_digit { hex_digit } .
没有二进制整型字面量。

浮点字面量(Floating-point literals)
浮点字面量是十进制表示的浮点常量,由整数、小数点、小数和指数部分组成:

float_lit = decimals “.” [ decimals ] [ exponent ] |
decimals exponent |
“.” decimals [ exponent ] .
decimals = decimal_digit { decimal_digit } .
exponent = ( “e” | “E” ) [ “+” | “-” ] decimals .
浮点数只有十进制表示方式,无八进制、十六进制、二进制表示方式;

浮点字面量示例:

  1. // 省略小数和指数
    72.40 // 省略指数
    072.40 // == 72.40 // 忽略开头的 0
    2.71828
    1.e+0
    6.67428e-11
    1E6 // 浮点数
    .25
    .12345E+5
    虚数字面量(Imaginary literals)
    imaginary_lit = (decimals | float_lit) “i” .
    Rune 字面量(Rune literals)
    rune 字面量代表一个 Unicode 代码点(code point)的整型值。

rune 是 int32 的别名(alias),两者可以通用。

rune 字面量用单引号包含,单引号内可以是除了单引号和换行符外的任意字符:单个字符代表它本身,反斜杠开头的多个字符有特殊含义。

rune_lit = “’” ( unicode_value | byte_value ) “’” .
unicode_value = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value = octal_byte_value | hex_byte_value .
octal_byte_value = \ octal_digit octal_digit octal_digit .
hex_byte_value = \ “x” hex_digit hex_digit .
little_u_value = \ “u” hex_digit hex_digit hex_digit hex_digit .
big_u_value = \ “U” hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit .
escaped_char = \ ( “a” | “b” | “f” | “n” | “r” | “t” | “v” | \ | “’” | " ) .
rune 中转义字符具有特殊含义,除了上面列出的转义字符外的其它转义字符是非法的:

字符串字面量(String literals)
字符串字面量代表字符串常量,有两种形式:原生字符串字面量(raw string literals)和可解释的字符串字面量(interpreted string literals)。

原生字符串字面量由反引号括起来,在反引号内部,除反引号外的其它字符都有效,转义字符没有特殊含义,可以包含换行(忽略\r)。

可解释的字符串字面量由双引号括起来,在引号内部不能包含回车,转义字符有特殊含义:

string_lit = raw_string_lit | interpreted_string_lit .
raw_string_lit = “" { unicode_char | newline } "” .
interpreted_string_lit = " { unicode_value | byte_value } " .
常量(Constants)
6 种常量:boolean、rune、interger、float、complex 和 string,其中 rune、integer、floating、complex 被称为数值常量,数值常量代表精确值,不会溢出。

组合类型如 struct、map、channel、array 不是常量。

预定义的标识符 true 和 false 代表 boolean 常量,预定义的标识符 iota 代表整形常量。

常量可能有类型(typed)或无类型(untyped),但变量一定是有类型的:

字面量常量、true、false、iota 和只包含无类型的常量运算数(operand)的常量表达式(constant expressions)的结果是无类型常量;
常量可以被赋予类型:显式的常量声明或转换、在变量声明、赋值或表达式中的运算数时被隐式赋予一个缺省类型;如果常量值不能代表该类型的值则会发生错误(如将 3.01 转换为 int 型);
无类型的常量具有缺省类型(default type),在需要类型值的场合(如变量声明、赋值或表达式中的运算数),常量将被隐式的转换为相应的缺省类型;如 i := 0,0 会被转换为 int 类型。常量的缺省类型是:bool、rune、int、float64、complex128 和 string。

注意常量和字面量的区别:字面量是常量的一种形式,字面量是无类型的,常量可以无类型,也可以有类型;

变量(Variables)
变量是值(value)的存储位置,变量的类型(type)决定了变量的取值范围。

程序运行过程中调用内置函数 new、获取组合字面量地址(如 &P{xxx})时会分配一个存储空间,结果可以作为变量使用,称为匿名变量,可以使用指针重定向(pointer indirection)访问它们。

结构化变量如 array、slice、struct 的成员有可能是可寻址的(匿名结构化变量的成员是不可寻址的),可作为变量使用(如在表达式或赋值语句的左侧使用);

map 的 value 是不可寻址的,因为 map 是动态扩容的,扩容后的地址可能与以前不一致,所以不支持表达式 m[“foo”].f = x,但是支持 m[“a”]++ 运算符;

变量的静态类型(简称类型)在声明变量、new、组合字面量、结构化变量的元素时指定。接口类型的变量还具有动态类型,该类型是赋值给该接口类型变量的值的类型,在运行过程中可以改变:

var x interface{} // x is nil and has static type interface{}
var v *T // v has value nil, static type *T
x = 42 // x has value 42 and dynamic type int
x = v // x has value (*T)(nil) and dynamic type *T
变量可以在声明时赋值,也可以在声明后赋值。变量值在表达式中被使用,变量如果没有被赋值,则它的值是对应类型的 zero value;

注意:slice、map、channel 声明后未赋值前的 zero value 是 nil,需要使用 make 函数进行初始化;

类型(Types)
类型决定取值范围和可用的操作,类型可以用类型名或类型字面量(组合已有的类型来定义一个新的类型)来定义。

boolean, numeric、string 类型名是预定义的,其它类型是通过类型声明语句(type declarations)定义的;

组合类型:可以使用类型字面量来定义 array、struct、pointer、function、interface、slice、map、channel 类型的组合类型;

Type = TypeName | TypeLit | “(” Type “)” .
TypeName = identifier | QualifiedIdent .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | SliceType | MapType | ChannelType .
每个类型 T 都有一个 underlying type:

如果 T 是预定义的命名类型或类型字面量,则 underlying type 是 T 本身;

否则,T 的 underlying type 是 T 指向类型的 underlying type,这种指向具有递归性,直到符合上条情况;

type T1 string type T2 T1 type T3 []T1 type T4 T3

The underlying type of string, T1, and T2 is string. The underlying type of []T1, T3, and T4 is []T1.

类型 T 的 underlying type 决定了它的操作特性(如可以使用的运算符等) ,underlying type 也影响赋值;

方法集(Method sets)
类型可能有关联的方法集:

接口类型的方法集是它本身;
非接口类型 T 的方法集是用接收类型 T(receiver type T)声明的 methods;
非接口类型 T 的指针类型 *T 对应的方法集是用接收类型 T 或 *T 的 methods;
struct 会继承嵌套的匿名 struct 类型的 field 的方法集;
所有类型均实现了空接口 interface {};
*T 类型的方法集比 T 大。

类型的方法集决定了该类型实现的接口,以及用相应类型作为接收类型时可以调用的方法;

类型的方法集应用场景:

将类型赋值给某接口类型时,该类型必须实现接口定义的所有方法,如函数的参数是接口类型 T 时,可以传入的值类型 X 或 *X 的方法集必须实现 T 定义的所有方法;
调用 struct 类型 T 中嵌入的匿名 struct 的方法;
布尔类型(Boolean types)
两个预定义的常量值 true 和 false,预定义类型是 bool;

数字类型(Numeric types)
数字类型表示整型或浮点类型的值,使用二进制补码表示,最高位为符号位:

对于有符号类型位数 n,有效范围为:-2^(n-1) ~ 2^(n-1)-1, -2^(n-1)为 -0;

对于无符号类型位数 n,有效范围为 0 ~ 2^n;

byte alias for uint8 rune alias for 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 是 unit8 的 alias,rune 是 int32 的 alias,可以相互通用;

字符串类型 (String types)
字符串是字节(Byte)序列,不可变;使用 len() 函数获取长度。不能对字符串和 array 值使用 cap()函数。

s[:3] 返回子字符串,字符串元素是不可以寻址的(故 &s[i] 操作非法)。

数组类型 (Array types)
数组是单一类型值的序列,序列的数量称为长度,长度是数组类型的一部分(相同值类型,但是不同长度的数组类型是不一致的)。

长度值表达式的结果必须是一个非负的,可以用 int 类型表示的常量值。

数组元素是可以寻址的。

切片类型 (Sliece types)
切片用于描述一个底层是一个数组的连续片段,它提供了存取这些有序序列的方式。切片是引用类型,未初始化赋值的切片类型值为 nil。

和数组一样,切片是可以索引的且有一个长度,且在执行过程中长度是可变的,可以通过 reslice 操作扩充其长度(只要不超过底层数组的长度即切片的容量即可)。

切片索引操作(index)的索引值必须满足: index >= 0 && index <= len(s),否则会 panic。

切片 和 reslice 操作后的切片共享相同的底层数组;

切片的元素是可以寻址的;

可以使用 make 函数来创建并初始化一个切片,容量参数是可选的(长度是必须参数):

make([]T, length, capacity)
下面两个语句等效:

make([]int, 50, 100)
new([100]int)[0:50]
对于多维的切片类型,内层的切片长度是可变的且必须要单独初始化;

切面字面量的长度和容量是 0,不能索引其成员,如:

v =: []int{} // v != nil,但是长度和容量都是 0
v[0] = 1 // panic
可以对 nil 切片使用 append 函数。

结构类型(Struct types)
结构是一系列命名元素的集合,这些元素称为域(field),具有名称和类型,没有名称的域称为嵌入式域(Embedded Field),结构的非空 (non-blank) 域名必须是各不相同。

StructType = “struct” “{” { FieldDecl “;” } “}” .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ “*” ] TypeName .
Tag = string_lit .
嵌入式域的类型可以为 T 或 *T,但是 T 本身不能为指针类型;

// A struct with four anonymous fields of type 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
}
结构 x 的匿名域的域或方法 f 可以提升(promoted)为 x 的域或方法,但是提升的域不能在 x 的组合字面量中直接引用,而应该将匿名域作为整体赋值如 x.f = xxx 是非法的,而应该使用 x.T3 = T3{f: xxx};

结构类型 S 和类型 T,S 包含的提升方法规则如下:

如果 S 包含匿名成员T,S 和 *S 的方法集包含提升的 T 的方法; *S 的方法集还包括提升的 T 的方法;
如果 S 包含匿名成员
T, S 和 *S 的方法集包含 T 和 *T 的方法集;
域的 tag 为空字符串时等效为没有指定 tag,tag 在 reflaction 时使用,并且作为结构类型定义的一部分。

示例:

type GetIpLocationsRequest struct {
Name string protobuf:"bytes,1,opt,name=name" json:"name,omitempty"
Cluster string protobuf:"bytes,2,opt,name=cluster" json:"cluster,omitempty"
Isps []string protobuf:"bytes,3,rep,name=isps" json:"isps,omitempty"
}
指针类型(Pointer types)
未初始化的指针类型值是 nil;

指针类似是可比较的,但是不能运算;

函数类型(Funciton types)
未初始化的函数类型值是 nil;

函数不能嵌套定义,但是可以在函数内部定义并使用匿名函数;

函数最后一个参数类型前可以加 … 符号,表示可变参数,在调用时,可以传入 0 个或多个相应类型的值,该参数会被声明为对应类型的切片。如果没有传入值,则切片值为 nil,否则为对应值的切片;

接口类型(Interface typs)
接口类型用于指定方法集;支持嵌套其它接口,但不支持嵌入接口本身,如果嵌套引起循环依赖则出错,如:

// illegal: Bad cannot embed itself
type Bad interface {
Bad
}

// illegal: Bad1 cannot embed itself using Bad2
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
如果接口定义了嵌套的接口包含的同名方法,则出错:

type ReadWriter interface {
Read(b Buffer) bool
Write(b Buffer) bool
}

type File interface {
ReadWriter // same as adding the methods of ReadWriter
Locker // same as adding the methods of Locker
Close()
}

type LockedFile interface {
Locker
File // illegal: Lock, Unlock not unique
Lock() // illegal: Lock not unique
}
接口值可以用于保存实现了该接口定义的所有方法集的类型值;

接口值为引用类型,未初始化的接口类型值为 nil;

映射类型(Map types)
未初始化的映射类型值是 nil。

不能像 nil map 添加元素,但是可以 index 操作获取结果;

可以使用 make 函数初始化映射:

make(map[string]int)
make(map[string]int, 100)
映射的 key 必须是可比较的,即支持 == 和 != 运算符,所以 function、map、slice 类型不能用作 key,否则可能引起 run-time panic;

m[“index”] 的结果是不可寻址的(因为 map 动态增长后,以前的地址就可能会无效),但支持 m[k]++ 运算符;

使用 delete 函数删除 map 元素;

如果元素不存在,m[index] 返回的结果是元素的 zero value,可以使用多重赋值来判断是否存在该元素;

通道类型(Channel types)
未初始化的通道类型值是 nil;channel 是引用类型,可以使用 make 函数初始化通道:

make(chan int, 100)
第二个参数指定通道的容量,同时也是通道缓冲区(buffer)的大小,如果值为 0 或未指定,则通道是非缓冲的,当发送者和接收者都准备好读写的时候才能通信;否则是缓存的通道,当通道没有满或空之前,发送者和接收者都可以通信;

nil 通道不能用于发送或接收,否则引起 runtime panic;

内置函数 close 关闭通道,读取关闭的通道时回对应元素类型的零值(zero value),可以使用多重赋值的接收运算符形式来判断通道是否被关闭。

len(chan T) 返回通道缓存的元素数目;cap(chan T )返回通道的容量(即传给 make 函数的参数值);

通道的方向运算符 <- 是向左最长关联的, 必要的时候需要用括号分隔:

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)
可以定义任何数据类型的通道!

类型和值的属性(Properties of types and values)
类型标识(Type identify)
Go 是强类型语言,两个类型相同 (identical) 或不相同 (different),类型影响可赋值性和可运算性;

使用 type 语句声明的两个类型不同(即 defined type 总是不同的),命名和非命名的类型不同;

满足下面条件的、具有相同 underlying type 的两个非命名类型相同:

数组元素类型和长度相同;

切片元素类型相同;

结构体的域名称、类型、顺序、tags 相同;如果有 Lower-case 的域且来源于不同 packages,则两个结构不同;

相同基类型的指针;

相同签名的函数,忽略参数名;

相同方法集合、相同函数类型的接口,不关心方法的顺序;

相同 key 和 value 类型的映射;

相同元素类型和方向的通道;

// 别名声明,新类型和原类型相同: type ( A0 = []string A1 = A0 A2 = struct{ a, b int } A3 = int A4 = func(A3, float64) *A0 A5 = func(x int, _ float64) *[]string )

type C0 = B0

// 类型声明,新类型和原类型各不相同: type ( 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 )

these types are identical:

A0, A1, and []string A2 and struct{ a, b int } A3 and int A4, func(int, float64) *[]string, and A5

B0, B0, and C0 []int and []int struct{ a, b *T5 } and struct{ a, b *T5 } func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5

B0 and B1 are different because they are new types created by distinct type definitions; func(int, float64) *B0 and func(x int, y float64) *[]string are different because B0 is different from []string.

使用 type C0 = B0 声明的两个类型相同(go 1.9 开始支持)。

B0 和 B1 是分别声明的两个类型,所以不同,但是 A0、B0 和 B1 具有相同的 underlying type,所以相互间可以强制转换;

可赋值性(Assignability)
在下列情况下,值 x 可以赋值给 T 类型的变量:

x 的类型和 T 一致;
x 的类型 V 和 T 具有相同的底层类型(underlying types),且 V 和 T 至少有一个是非命名类型(如字面量值或字面量类型);
T 是接口类型,x 实现 T;
x 是一个通道值,T 是通道类型,x 的类型 V 和 T 有相同的元素类型且 V 和 T 至少有一个是非命名类型;
x 是预定义标识符 nil,T 是指针、函数、切片、映射、通道或接口类型;
x 是无类型的常量,且可被 T 类型值所代表 (representable),如: var i int32 = 10.0
这里的可代表的含义是:无类型数值常量 (int 或 float) 可以赋值给有类型数值变量,只要变量类型能容纳下对应的常量值即可;

var i = int(10.0) // 正确 const i = int(10.0) // 错误 var k = i * int(45.6) // 错误

块 (Blocks)
块是大括号包含语句 (Statements) 列表(可以为空);

Block = “{” StatementList “}” .
StatementList = { Statement “;” } .

Statement =
Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
语句 (Statement) 以分号结束,包含声明、空语句、表达式语句、发送语句、自增和自减语句、赋值语句、短变量声明语句等;

块也是种特殊的语句;

除了显式的大括号定义的块外,下列情况隐式地定义块,在其中声明的常量、变量、类型等会隐藏外面的定义:

universe block 包含所有 Go 源文件;
每个包定义一个块,包含该包的所有源文件;
每个文件定义一个块,包含该文件的所有内容;
各 if、for、switch 语句有各自的隐式块;
switch、slect 语句的各子句有各自的隐式块;
注意:4 和 5 意味着在 if、for 等语句定义的变量,只在该语句块中有效,语句结束后变量就被 GC 回收,如:

for i := 1; i < 10; i++ {
}
变量 i 只在 for 循环内部有效(i 可能会隐藏 for 前面声明的同名变量),for 循环外不能使用定义的 i 变量。

块影响作用域,变量的作用域和生命期不是一个概念:

块决定变量的作用域,作用域指的是该标识符的可见(引用、有效)范围;
生命期是标识符引用的对象的存在时间,生命期由 GC 控制,当变量的内存区域的引用计数为 0 时,GC 将销毁对应的内存区域;
声明和作用域(Declarations and scope)
声明是一种特殊的语句(statement)。

声明 (Declartion) 将非 blank 的标识符与常量、类型、变量、函数、方法、标签或 package 名称绑定;

程序中的所有标识符都需要声明;
不允许在同一个 block 中重复声明标识符;
不允许在文件和 package block 中重复声明同一个标识符;
blank 标识符可以在声明语句中使用,但是不引入新的绑定 (binding),不是声明语句,故 _ := 2 或者 var _ int = 2 是错误的;

在 package block,init 标识符只能用于声明 init 函数,和 blank 标识符一样,它不引入新的绑定,故包里可以声明多个 init 函数;

Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
Go 使用基于块的词法作用域,作用域决定了标识符的有效使用范围(注意,与标识符的生存期是有区别的,后者是运行时特性,由 GC 控制):

预定义的标识符是全局作用域;
Top level 声明的常量、类型、变量、函数(非方法)是包级别作用域(所有函数外部,顺序无关,使用的位置可以在声明前);
导入的包名是文件作用域,只在当前导入的文件中有效;
方法接收者、函数参数、结果变量是函数体作用域;
在函数体内定义的常量或者变量,从开始定义的位置到最内层block一直有效;
在函数体内定义的类型,从开始定义的位置到最内层block一直有效;
外层 block 定义的标识符,可以在内层重新定义,内层的定义将隐藏外层的定义;

package 子句不是声明语句,不引入任何绑定,它的作用是指定属于同一个 package 的源文件,同时用作后续 import 该 package 的名称;

标签作用域(Label scopes)
标签用于 break、continue 和 goto 语句;可以定义未使用的 label,label 不是 block 作用域,不与同名的非 lable 的标识符冲突;

标签的作用域是定义它的函数体,不包括嵌套的函数,故可以在函数内先使用 label,再定义 label;

空标识符(Blank identifier)
空标识符用下划线 _ 表示;

预定义标识符(Predeclared identifiers)
Types:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr

Constants:
true false iota

Zero value:
nil

Functions:
append cap close complex copy delete imag len
make new panic print println real recover
这些预定义的标识符在全局作用域中有效;

导出的标识符(Exported identifiers)
可以在其它包中使用包导出的标识符。满足下面两个条件的标识符被导出:

标识符的第一个字符是大写字母;
标识符位于 package 级别,或者是成员或方法名;
其它类型的标识符都不会被导出;

标识符的独特性(Uniqueness of identifiers)
标识符的独特性用于表示两个标识符是否相同。

不同拼写的两个标识符、位于不包中且未导出的标识符是不相同,其它情况是相同的;

常量声明(Constant declarations)
常量声明是将标识符与**常量表达式(constant expressions)**绑定;

ConstDecl = “const” ( ConstSpec | “(” { ConstSpec “;” } “)” ) .
ConstSpec = IdentifierList [ [ Type ] “=” ExpressionList ] .

IdentifierList = identifier { “,” identifier } .
ExpressionList = Expression { “,” Expression } .
如果指定了类型,表达式值必须可赋值到对应类型(assignable to that type);

如果忽略了类型,则常量为各表达式结果的类型;如果表达式结果是无类型常量,则声明的常量也是无类型的:

const Pi float64 = 3.14159265358979323846
const zero = 0.0 // untyped floating-point constant
const (
size int64 = 1024
eof = -1 // untyped integer constant
)
const a, b, c = 3, 4, “foo” // a = 3, b = 4, c = “foo”, untyped integer and string constants
const u, v float32 = 0, 3 // u = 0.0, v = 3.0
在括起来的常量声明列表表达式语句中,除了第一个声明外后续的类型和表达式列表可以省略,这时相当于第一个类型和表达式的文本替换,如:

const ( Sunday = iota Monday // 后续的常量是前一个类型和表达式的重复; Tuesday Wednesday Thursday Friday Partyday numberOfDays // this constant is not exported )

iota
iota 通常用于 const enum 列表的定义中:

代表持续增长的无类型整形常量;

const 修饰符重置 iota 的值为0;

const ( // iota is reset to 0 a = 1 << iota // a == 1 b = 1 << iota // b == 2 c = 3 // c == 3 (iota is not used but still incremented),注意:虽然没有使用 iota,但是它继续增长; d = 1 << iota // d == 8 )

const ( // iota is reset to 0 u = iota * 42 // u == 0 (untyped integer constant) v float64 = iota * 42 // v == 42.0 (float64 constant) w = iota * 42 // w == 84 (untyped integer constant) )

在一个表达式内部,iota 的值是相同的,因为 iota 只在新的 const 表达式中递增:

const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0,两个iota值相同;
bit1, mask1 // bit1 == 2, mask1 == 1
_, _ // skips iota == 2
bit3, mask3 // bit3 == 8, mask3 == 7
)
类型声明(Type declarations)
类型声明将标识符和类型绑定,有两种形式:别名声明、类型定义;

TypeDecl = “type” ( TypeSpec | “(” { TypeSpec “;” } “)” ) .
TypeSpec = AliasDecl | TypeDef .
别名声明(Alias declarations)
别名声明将标识符和给定的类型绑定(注意标识符和类型间的等号):

AliasDecl = identifier “=” Type .
在标识符的作用域范围内,该标识符是对应类型的别名:

type (
nodeList = []*Node // nodeList and []*Node are identical types
Polar = polar // Polar and polar denote identical types
)
类型定义(Type definitions)
类型定义创建一个新的、不同的类型,所有可以用于 underlying 的操作都可以运用于新的类型:

TypeDef = identifier Type .
新的类型与任何其它类型都不相同(包括用于创建它的类型);

声明的新类型不继承已有类型的任何方法;但是接口类型的方法集,组合类型的成员&方法集不变:

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct { /* Mutex fields */ }
func (m Mutex) Lock() { / Lock implementation */ }
func (m Mutex) Unlock() { / Unlock implementation */ }

// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex // 新类型不继承旧类型的任何方法

// The method set of the base type of PtrMutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex // 新类型不继承旧类型的任何方法

// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its anonymous field Mutex.
type PrintableMutex struct { // 新类型包含匿名 field 的成员和方法
Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block // Block 是接口类型,新类型和Block具有相同的方法集;
类型定义创用于定义不同的 boolean, numeric, string 类型,同时给它们关联方法:

type TimeZone int

const (
EST TimeZone = -(5 + iota)
CST
MST
PST
)

func (tz TimeZone) String() string {
return fmt.Sprintf(“GMT%+dh”, tz)
}
变量声明(Variable declarations)
变量声明用于创建一个或多个变量,并给各变量赋初始值;没有赋值的变量被初始化对应的 zero value;

将无类型的常量赋值给变量时,会先被转换为它的 default type(注意,整数的default type是 int,而非 int64,浮点数的 default type 是 float64);

nil 不能给无类型的变量初始化赋值:

var d = math.Sin(0.5) // d is float64
var i = 42 // i is int
var t, ok = x.(T) // t is T, ok is bool
var n = nil // illegal。因为 nil 是无类型值。
var p *int = nil // ok
注意:编译器可能不允许在函数体内声明未使用的变量。

短变量声明(Short variable declarations)
ShortVarDecl = IdentifierList “:=” ExpressionList .
短变量声明语句可能会重新声明 (redeclare) 位于同一个块(包括函数参数和返回值)的变量,它们的类型相同且至少有一个非空的变量;

只允许使用短变量声明的形式重新声明变量,这时重新声明的变量不会引入新的变量,只是对原变量赋值;

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // redeclares offset
a, a := 1, 2 // illegal: double declaration of a or no new variable if a was declared elsewhere
只能在函数内部使用短变量声明语句;在if、for、switch 等支持子 block 的语句中,可以创建只在对应子句有效的临时变量;

函数声明 (Funciton declaration)
函数声明将标识符(函数名)与函数绑定:

FunctionDecl = “func” FunctionName ( Function | Signature ) .
FunctionName = identifier .
Function = Signature FunctionBody .
FunctionBody = Block .
如果函数签名包含返回结果参数,则函数体必须以终止语句 (terminating statement) 结束;

func IndexRune(s string, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
// invalid: missing return statement
}
函数声明可以忽略函数体,这表示该函数在 Go 外部实现(如 assembly routine,runtime)

func flushICache(begin, end uintptr) // implemented externally,忽略了大括号 body,而不是空 body。
方法声明(Method declarations)
方法声明是带接收者的函数:

MethodDecl = “func” Receiver MethodName ( Function | Signature ) .
Receiver = Parameters .
Receiver 对应的 Parameters 必须是单一的非可变类型的参数,类型为 T 或 *T, T 不能为指针或接口类型,T 被称为基类型(base type)。

如果函数体内不使用 Receiver 值时可以忽略 value 标示符(只提供 type);

如果 Receiver 为指针类型,则可以在方法体内修改 Receiver 的值;

对于 base type,所有 non-blank 的方法名称必须各不相同。如果 base type 是 struct 类型,则 non-blank 的方法名和 field 名称必须各不相同;

表达式(Expression)
表达式:使用运算符 (operators) 和函数对运算数 (operands) 进行运算,获得运算结果;

运算数(Operands)
Operands 为表达式中的元素 value,可以是字面量 (literal)、非 blank 的标识符(代表常量、变量、函数等)、生成函数的方法表达式 (method exporession) 或括起来的表达式;

Operand = Literal | OperandName | MethodExpr | “(” Expression “)” .
Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent.
bool 类型包含两个预定义的常量值 true 和 false,归于上面的 OperandName 中;

blank 标识符用于运算数时,只能用于赋值语句的左边。

完备标识符(Qualified identifiers)
完备标识符指的是用包名作为前缀的标识符;

QualifiedIdent = PackageName “.” identifier .
完备标识符用于访问其它包(必须已经导入)中的标识符,标识符必须被导出且在 package block 声明。

组合字面量(Composite literals)
组合字面量包括 structs、arrays、slices 和 map,各组合字面量不相同,组合字面量由大括号分割的元素列表组成,每个元素前面可以有个 key;

CompositeLit = LiteralType LiteralValue .
LiteralType = StructType | ArrayType | “[” “…” “]” ElementType |
SliceType | MapType | TypeName .
LiteralValue = “{” [ ElementList [ “,” ] ] “}” .
ElementList = KeyedElement { “,” KeyedElement } .
KeyedElement = [ Key “:” ] Element .
Key = FieldName | Expression | LiteralValue .
FieldName = identifier .
Element = Expression | LiteralValue .
对于 struct 字面量而言,key 为 fieldname (即标示符 identify); 对于 array 和 slice 字面量而言,key 为 index; 对于 map 而言为 key;index 和 key 可以是表达式或者字面量值;

在组合字面量中,不能多次对同一个 field name 或 constant key value 赋值。

对于 struct 字面量而言:

key 必须是 struct 的 fieldname;
如果字面量不包含 key,则必须按顺序列出所有 field 的值;
如果一个元素有 key,其它元素也必须用 key 指定;
如果用 key 指定元素,则不必列出所有 key,未列出的 key 值为对应的 zero 值;
如果忽略 struct 的元素列表,则每个元素为对应的 zero 值;
不能给其它 package 中未export 的 field 赋值;
对于 slice 和 array 字面量而言:

每个元素关联一个整型 index,表示它在 array 中的位置;
如果一个元素指定了 index,则它必须是整型表达式(不一定是常量);
如果元素没有指定 index,则它的 index 为前一个元素的 index + 1,第一个元素的 index 值为 0;
注意:对于 slice 和 array 字面量,如果某个 value 指定了 index,没有强制要求其它 value 也必须指定 index。

组合字面量是可寻址的,寻址运算符会初始化一个新的变量,返回它的地址;

组合字面量支持如下的简写形式:

[…]Point{{1.5, -3.5}, {0, 0}} // same as […]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}} // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}} // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{“orig”: {0, 0}} // same as map[string]Point{“orig”: Point{0, 0}}
[…]*Point{{1.5, -3.5}, {0, 0}} // same as […]*Point{&Point{1.5, -3.5}, &Point{0, 0}},自动获取地址;
map[Point]string{{0, 0}: “orig”} // same as map[Point]string{Point{0, 0}: “orig”}
如果在 if、for、switch 的子句中使用 TypeName 类型的字面量,则需要使用括号,防止引起歧义:

if x == (T{a,b,c}[i]) { … } // 如果不加括号则解释为: 将 x 和 T进行比较,函数体为 {a, b, c}, 后续出现语法错误;
if (x == T{a,b,c}[i]) { … }

// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
函数字面量(Function literals)
函数字面量代表匿名函数;匿名函数具有闭包 (closures) 特性(可以引用外围函数定义的变量):

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)
注意:下面使用匿名函数的方式是错误的:

for i, file = range os.Args[1:] {
wg.Add(1)
go func() {
compress(file)
wg.Done()
}()
}
因为 go 语句是延迟执行的,函数内的 file 引用的是执行时的 file 变量值,而不是循环时的 file 值。

解决方法是将 file 作为参数传给 goroutine 函数,或者调用 goroutine 前将 file 赋值给临时变量,在 groutine 内部使用该临时变量。

主表达式(Primary exprssion)
主表达式是单目或双目运算符的运算数 (operands),故优先级比运算符高,如表达式:(*t.T0).x :

t.T0 是主表达式,*t.T0 是对主表达式使用指针重定向单目运算符,(*t.TO) 整体作为一个运算数,然后 (*t.T0).x 也是主表达式。

PrimaryExpr =
Operand |

Conversion |
PrimaryExpr Selector |
PrimaryExpr Index |
PrimaryExpr Slice |
PrimaryExpr TypeAssertion |
PrimaryExpr Arguments . // 函数调用

Selector = “.” identifier .
Index = “[” Expression “]” .
Slice = “[” [ Expression ] “:” [ Expression ] “]” |
“[” [ Expression ] “:” Expression “:” Expression “]” .
TypeAssertion = “.” “(” Type “)” .
Arguments = “(” [ ( ExpressionList | Type [ “,” ExpressionList ] ) [ “…” ] [ “,” ] ] “)” .
类型转换、选择、索引、slice、类型断言、函数调用都是 PrimaryExpr,优先级比单目和双目运算符高;

选择符(Selectors)
x 不是 package name,x.f 代表值 x(或 *x) 的成员或方法 f。

选择符 f 可能代表类型 T 的成员或方法,也可能是类型 T 嵌套成员的成员或方法,嵌套的次数称为深度:

x 是 T 或 *T 类型的值且 T 不是指针或接口类型,x.f 代表 T 最浅深度的成员或方法,如果同一深度有多个相同名成员或方法,则非法。
不考虑 T 或 *T 类型的方法集,因为 T 不是接口类型;
如果 x 是接口类型 I 的值,x.f 代表接口动态类型对应的成员或方法,f 必须位于 I 的方法集中;
1 和 2 条件的特殊情况:如果 x 是命名的指针类型且 (*x).f 是一个有效的成员 (非 method),x.f 和 (*x).f 等效;
对于条件 1,如果 T 不是接口类型或指针类型:

对于 T 类型的值 x,x 可以调用在 T 或 *T 上定义的方法,即 T 类型值 x 可以调用 receiver 是 *T 或 T 的方法;
对于 *T 类型的值 x,x 可以调用在 T 或 *T 上定义的方法;
其它情况下,x.f 是非法的。

如果 x 是指针类型且值为 nil,x.f 如果代表 struct field,则读取或赋值 x.f 将引起 runtime panic;
如果 x 是接口类型且值为 nil,x.f 如果代表方法,则调用 x.f 时将引起 runtime panic;
示例:

type T0 struct {
x int
}

func (*T0) M0()

type T1 struct {
y int
}

func (T1) M1()

type T2 struct {
z int
T1
*T0 //命令的指针类型
}

func (*T2) M2()

type Q *T2 //命名的指针类型

var t T2
var p *T2
var q Q = p

one may write:

t.z // t.z
t.y // t.T1.y
t.x // (*t.T0).x t.T0是命名的指针类型且(*t.T0).x是有效的选择符,所以可以使用t.x缩写形式;

p.z // (*p).z p是命名的指针类型*T2,且 (*p).z有效,故可以使用p.z缩写形式;
p.y // (p).T1.y
p.x // (
(*p).T0).x

q.x // (*(*q).T0).x (*q).x is a valid field selector

p.M0() // ((*p).T0).M0() M0 expects *T0 receiver
p.M1() // ((*p).T1).M1() M1 expects T1 receiver
p.M2() // p.M2() M2 expects *T2 receiver
t.M2() // (&t).M2() M2 expects *T2 receiver, see section on Calls 注意这种有效形式,自动传入t的地址!

but the following is invalid:

q.M0() // (*q).M0 is valid but not a field selector
Method expressions
如果 M 是类型 T 方法集中一个方法,则 T.M 可以像普通函数一样使用,只不过第一个参数为方法集中M的接收者:

type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver

var t T

T.Mv 生成一个函数,等效于:

func(tv T, a int) int //第一个参数为 T 方法集中 Mv 的接收者类型;

其它调用方式:

t.Mv(7)
T.Mv(t, 7) // 方法表达式调用
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

类似的:

(*T).Mp // 注意,T.Mp 是无效的,因为 T 类型的方法集中不包含 Mp 方法;同时 (*T).Mp 的括号是不可省略的,否则等效为: *(T.Mp)。

等效于:

func(tp *T, f float32) float32

由于 *T 的方法集中包含 T 作为参数的方法 Mv,故下面的表达式也是有效的:

(*T).Mv // 有效!

等效于:

func(tv *T, a int) int // go 根据传入的地址 tv 创建一个变量,函数不会修改通过地址传进来的参数;
Method values
如果值 t 的静态类型 T 有方法 M,则 t.M 被称为方法值,它是一个可以调用的函数;

type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver

var t T
var pt *T
func makeT() T

t.Mv 生成一个函数:func(int) int

f := t.Mv; f(7) // like t.Mv(7)
f := pt.Mp; f(7) // like pt.Mp(7)
f := pt.Mv; f(7) // like (*pt).Mv(7) // 自动转换;
f := t.Mp; f(7) // like (&t).Mp(7) // 自动转换;
f := makeT().Mp // invalid: result of makeT() is not addressable

var i interface { M(int) } = myVal
f := i.M; f(7) // like i.M(7)
Index expressions
主表达式 a[x] 被称为索引表达式。a 可以是数组、数组的指针(非 slice 指针)、slice、字符串和 map;

如果 a 不是 map 类型: x 必须是整型或者无类型数字,必须位于 0 <= x < len(a),否则 runtime panic;
如果 a 是 array 类型的指针: a[x] 等效于 (*a)[x];
如果 a 是 string 类型:a[x] 结果是非常量 byte (non-constant byte value) 类型,不可以给 a[x] 赋值;a[x] 是不可以寻址的;
如果 M 是 map 类型且为 nil(未初始化的 map)或者不包含 x 对应的元素,则 a[x] 返回对应的zero value;如果向 nil map 插入值,则 runtime panic;
其它情况下 a[x] 是非法的。例如 a 是指向 slice、map、字符串的指针则 a[x] 是非法的。

Slice expressions
slice 表达式产生一个子字符串或者 slice,可以应用于 string、数组、数组指针、slice;

simple slice expressions
a[low : high]

high >= low >= 0; index 不能是负值,不支持倒序索引;

a[2:] // same as a[2 : len(a)]
a[:3] // same as a[0 : 3]
a[:] // same as a[0 : len(a)]
如果 a 是 array 的指针,则 a[low : high]等效于 (*a)[low : high];
如果 a 是 array 或 strings,indices 应该位于 0 <= low <= high <= len(a),否则 runtime panic;
对于 slice 而言,上边界取决于 cap(a) 的值,而非 length;

除了无类型的 string,对 string 和 slice 进行 slice 操作后,结果还是非常量的 string 或 slice 类型值;

对 array 进行 slice 操作时,该 array 必须是可寻址的,结果是 slice 类型。

full slice expressions
只对 array、array 指针、slice,不包含 string,主表达式:

a[low : high : max]
结果为相应类型的 slice,容量为 max-low; 只有 low 可以省略,默认为 0;

如果是 array,它必须是可寻址的;

0 <= low <= high <= max <= cap(a), 否则 runtime panic;

Type assertions
x.(T):表达式 x 的结果必须是接口类型且不能为 nil;如果 aeesertion 的结果为 fase,则会引起 run-time panic,解决办法是使用多变量赋值的语句判断:

var v, ok T1 = x.(T)
Calls
函数的形参必须是 single-valued 表达式,且 assignable to 函数的参数类型。在调用函数前,先执行传递的表达式参数。 调用 nil 函数将引起 run-time panic; 如果函数的返回参数数目和类型与另一个函数的匹配,则可以将函数作为因一个函数的参数:f(g(parameters_of_g)); + 如果 f 的最后一个参数 v 是 … 类型,则它的内容是 g 函数返回值各自赋值给 f 后剩余的结果;

如果 x 的方法集中包含 m,则 x.m() 合法;如果 x 是可寻址的且 &x 的方法集中包含 m,则 x.m() 等效于 (&x).m();

Passing arguments to … Parameters
如果函数最后一个参数 p 类型是 …T,则在函数内部 p 的类型等效于 []T,如果函数调用时没有给 p 传入参数则 p 的值为 nil;

注意:只有当函数最后一个参数是可变类型且传入的 slice 参数类型与之一致时,才能使用 s… 的形式将整个 slice 传入函数;

Operators
运算符将运算数组合为表达式;

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr = PrimaryExpr | unary_op UnaryExpr .

binary_op = “||” | “&&” | rel_op | add_op | mul_op .
rel_op = “==” | “!=” | “<” | “<=” | “>” | “>=” .
add_op = “+” | “-” | “|” | “^” .
mul_op = “*” | “/” | “%” | “<<” | “>>” | “&” | “&^” .

unary_op = “+” | “-” | “!” | “^” | “*” | “&” | “<-” .
对于双目运算符,运算数类型必须一致,例外情况是移位运算符的运算数或运算数是无类型的常量;

除了移位运算,如果一个运算数是无类型常量,另一个运算数不是,则无类型常量会被先转换为另一个运算数的类型;

++ 和 – 是语句而不是运算符。

Operator precedence
单目运算符具有最高优先级; ++ 和 – 是语句而不是运算符,所以不在运算符优先级考虑的范围内,*p++等效于 (*p)++。

双目运算符有 5 级优先级:

Precedence Operator
5 * / % << >> & &^ // 乘性算术运算符(包括位运算)
4 + - | ^ // 加性算术运算符
3 == != < <= > >= // 关系
2 && // 逻辑
1 ||
左移和右移 n 位,分别相当于乘以 2^n 或除以 2^n,所以 << 和 >> 属于乘性运算符,和算术的乘、除优先级一致;
^ 按位异或; &^ 按位同或;
Arithmetic operators
算术运算符适用于数值,结果为第一运算数的类型。

/ 适用于整形、浮点型和虚数类型,+ 还适用于字符串类型;位运算只适用于整形;
Integer operators
q = x / y and remainder r = x % y 满足:

x = q*y + r and |r| < |y|
如果除数是常量,则不能为 0,否则会造成 run-time panic。 如果除数不是 constant,则可以为 0,结果可能为 math.Infinte,math.NaN等。

余数的符号与 x、y 的符号无关,需要根据上面的表达式确定。

Integer overflow
对于无符号整型,算术运算的结果是 module 2^n, n 为无符号类型的宽度(位数),如果结果超过了 2^n,则超过的(溢出的)部分将被丢弃,程序需要能处理 wrap around 的情况;

对于有符号类型,算术运算的结果是 module 2^(n-1),如果超过了该值,则可能溢出到符号位,正值可能变为负值,负值也可能变为正值;

var u uint8 = 255 fmt.Println(u, u+1, uu) // “255 0 1” // u +1 或 uu 的结果溢出,结果变小 (wrap-around)

var i int8 = 127 fmt.Println(i, i+1, i*i) // “127 -128 1” // i+1 的结果:1000 0000,1溢出到符号位,故结果为 -128;

overflow 时不会产生异常。

String concatenation
字符串相加或 += 的结果为新的字符串;

Comparison operators
比较运算符用于比较两个运算数,结果为无类型的 boolean。

比较的两个运算数,必须满足:

第一个运算数可以赋值给(assigned)第二个运算数;
或者反之;
支持 == 和 != 的运算数是可比较的 (comparable); 支持 <、<=、>、>= 的运算数是有序的(ordered); 只有整型、浮点型、字符串类型值是有序的;

对于同时指向 zero-size 值的指针变量,可能相等也可能不相等; 比较两个 struct 时,只考虑其中的非 blank 的域是否相等;

slice、map 和 function value 是不可以比较的(所以对应的类型值不能作为 map 的 key),但是它们可以和 nil 进行比较。 也允许 pointer、channel、interface value 和 nil 进行比较。

如果两个接口类型值不可比较,则会引起 runtime panic。

Logical operators
逻辑运算符对布尔值进行操作,运算符右边的运算数是条件执行的。

Address operators
使用寻址运算符的运算数必须是可寻址的:

变量;
指针重定向后的值;
slice 索引值;
可寻址 struct 的 field;
可寻址 array 的索引值;
组合字面量
如果x是nil指针,访问*x将引起run-time panic;

Receive operator
从 channel 接收数据的运算符表达式将阻塞,直到有数据可用,从 nil channel 接收数据将一直被阻塞。

从 closed 的 channel 接收数据将立即返回,返回的结果为对应类型的 zero value (当 channel 中所有的数据都被接收后)。通过使用多重赋值表达式可以检查 channel 是否关闭:

var x, ok = <-ch
receive operator 是一个表达式,故可以用在各语句中,而向 channel 发送数据则是一条语句;

Conversions
Conversion = Type “(” Expression [ “,” ] “)” .
转换属于 PrimaryExpr,优先级比运算符高。

如果类型转换的类型以 * 或 <- 开始,或者类型以 func 开始且无返回值列表,则必须要用括号扩住类型,以防止歧义:

*Point§ // same as *(Point§)
(*Point)§ // p is converted to *Point
<-chan int© // same as <-(chan int©)
(<-chan int)© // c is converted to <-chan int
func()(x) // function signature func() x
(func())(x) // x is converted to func()
(func() int)(x) // x is converted to func() int
func() int(x) // x is converted to func() int (unambiguous)
常量类型值 x (数值、字符串、布尔值)可以转换为类型 T 的情况:

x 是类型 T 可代表 (representable) 的值; // int(1.2) 非法,因为整型不能代表浮点型常量;
x 和 T 都是浮点类型;
x 是整型,T 是字符串类型:x 是整型值(常量或者变量)时,可以转换为 string 类型,结果为 x 代表的 Unicode code point 的单字符串。
nil 不是常量。

uint(iota) // iota value of type uint
float32(2.718281828) // 2.718281828 of type float32
complex128(1) // 1.0 + 0.0i of type complex128
float32(0.49999999) // 0.5 of type float32
float64(-1e-1000) // 0.0 of type float64
string(‘x’) // “x” of type string
string(0x266c) // “♬” of type string
MyString(“foo” + “bar”) // “foobar” of type MyString
string([]byte{‘a’}) // not a constant: []byte{‘a’} is not a constant
(*int)(nil) // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
int(1.2) // illegal: 1.2 cannot be represented as an int
string(65.0) // illegal: 65.0 is not an integer constant
非常量类型值 x 可以转换为类型 T 的场景:

x assignable to T;
忽略 struct tags,x 的类型和 T 有相同的 underlying 类型;
忽略 struct tags,x 的类型和 T 都是 unnamed point,且它们的指针基类型有相同的 underlying 类型;
x 和 T 都是整型或浮点型;
x 和 T 都是虚数类型;
x 是整型、byte 或 rune 的 slice,T 是 string 类型;
x 是 string 类型,T 是 byte 或 rune 的 slice 类型;
整数和字符串间转换时,会改变原始值,其它情况的转换,只是修改了 value 的代表形式,value 本身并不改变;

指针和整型是有区别的,unsafe package 提供了相关的函数可以在两者之间转换。

Conversions between numeric types
非常量数值间的转换:

对于整形类型间的转换:
如果值是有符号整形,则符号位将扩展结果类型所需的宽度;
如果值是无符号整型,则用 0 扩展到结果类型所需的宽度; v := uint16(0x10F0) int8(v) 的结果是 1111 0000,最高位的 1 为符号位; uint32(int8(v)): 由于 int8 是有符号整形,故符号位 1 将扩展到 32 位,结果为 0xFFFFFFF0;
浮点数转为整数时,小数部分将被忽略;
如果整形或浮点数转为浮点数,则结果精度取决于目标类型;
Constant expressions
常量表达式只包含常量运算数且在编译时执行。

Untyped boolean, numeric, and string constants may be used as operands wherever it is legal to use an operand of boolean, numeric, or string type, respectively.

注意:这里对 numeric 没有区分是那种类型,所以在需要数值的地方就可以使用无类型的 constant(int32、int64、float32、float64)。

除了移位运算符,如果双目运算符两边均是无类型常量,则运算结果和运算符右边的无类型常量一致,如 整型常量 / 复数常量 的结果是复数常量;

常量比较的结果是无类型的boolean常量;如果位运算的左侧是无类型常量,则结果是整形常量;其它无类型常量的运算结果是同类型的无类型常量;

const a = 2 + 3.0 // a == 5.0 (untyped floating-point constant)
const b = 15 / 4 // b == 3 (untyped integer constant)
const c = 15 / 4.0 // c == 3.75 (untyped floating-point constant)
const Θ float64 = 3/2 // Θ == 1.0 (type float64, 3/2 is integer division)
const Π float64 = 3/2. // Π == 1.5 (type float64, 3/2. is float division)
const d = 1 << 3.0 // d == 8 (untyped integer constant)
const e = 1.0 << 3 // e == 8 (untyped integer constant)
const f = int32(1) << 33 // illegal (constant 8589934592 overflows int32)
const g = float64(2) >> 1 // illegal (float64(2) is a typed floating-point constant)
const h = “foo” > “bar” // h == true (untyped boolean constant)
const j = true // j == true (untyped boolean constant)
const k = ‘w’ + 1 // k == ‘x’ (untyped rune constant)
const l = “hi” // l == “hi” (untyped string constant)
const m = string(k) // m == “x” (type string)
const Σ = 1 - 0.707i // (untyped complex constant)
const Δ = Σ + 2.0e-4 // (untyped complex constant)
const Φ = iota*1i - 1/1i // (untyped complex constant)

3.14 / 0.0 // illegal: division by zero
uint(-1) // -1 cannot be represented as a uint
int(3.14) // 3.14 cannot be represented as an int
int64(Huge) // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300 // operand 300 cannot be represented as an int8 (type of Four)
Four * 100 // product 400 cannot be represented as an int8 (type of Four)
Order of evaluation
在 package 级别,初始化依赖 决定了变量声明中各初始化表达式的执行顺序。否则,当执行表达式、赋值、return 等语句中包含的运算数时,函数调用、方法调用、通信操作三类表达式的的顺序是语法上自左向右;

y[f()], ok = g(h(), i()+x[j()], <-c), k()
上面的函数调用、通信操作的顺序是: f(), h(), i(), j(), <-c, g(), and k(),但是对于 x 的索引操作和 y 的索引操作的执行顺序是未定义的。

a := 1
f := func() int { a++; return a }

x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
n := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified
在 package 级别,初始化依赖决定各初始化表达式的执行顺序(而不是默认的自左向右规则);

var a, b, c = f() + v(), g(), sqr(u()) + v()

func f() int { return c }
func g() int { return a }
func sqr(x int) int { return x*x }

// functions u and v are independent of all other variables and functions

函数调用的执行顺序是 u()、sqr()、v()、f()、v()和g();
Statements
语句控制执行;

Statement =
Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
Terminating statements
终止语句指的是终止后续语句的执行,包括:

return 或 goto 语句;
调用内置的 panic 函数;
以 terminating statement 结尾的 block;
满足如下条件的 if 语句:
包含 else 分支,且两个分支都以 terminating statements 结尾。
满足如下条件的 for 语句:
语句体中不包含 break 语句;
缺少 loop 条件,即死循环;
满足如下条件的 switch 语句:
语句体中不包含 break 语句;
有一个 default 子句;
所有子句都以 terminating statements 结尾(可能以 “fallthrough” 结尾);
满足如下条件的 select 语句:
语句体中不包含 break 语句;
各个case(包括default)必须都终止,该select才能终止后续语句的执行;
标记 terminating statement 语句的 label 语句;
其它语句都不是 terminating 语句。

Empty statements
空语句。

EmptyStmt = .
Labeled statements
标签语句用于标记 goto、break、continue 语句的目标(target)。

LabeledStmt = Label “:” Statement .
Label = identifier .

Error: log.Panic(“error encountered”)
经常用于 for 关键字前,用于跳出多重循环。

Expression statements
ExpressionStmt = Expression .
除了下列的内置函数外,函数、方法和接收运算符可以出现在语句上下文中(单行),原因是这些内置函数返回的变量值是无类型的,而 Go 要求每个变量必须有类型:

append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
如果直接调用上面的内置函数,会出现类似于 ./test.go:4: len(“1234”) evaluated but not used 的错误。

h(x+y)
f.Close()
<-ch
(<-ch)
len(“foo”) // illegal if len is the built-in function
Send statements
SendStmt = Channel “<-” Expression .
Channel = Expression .
channel 表达式 和 value 表达式在通信前执行,向关闭的 channel 发数据会引起 runtime panic。

向 nil channel 发数据会一直被阻塞,编译器会检查到这种状态:

package main

func main() {
var c chan int
c <- 1
}

$ go run test.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
/Users/xxx/test.go:5 +0x49
exit status 2
向 channel 发送数据是语句,而从 channel 接收数据是表达式。

IncDec statements
IncDecStmt = Expression ( “++” | “–” ) .
表达式的结果必须是可寻址的,或者是 map 索引表达式(但是 map 索引表达式的结果是不可寻址的)。

自增自减是语句而非表达式,且只能用在表达式的后面。

Assignments
Assignment = ExpressionList assign_op ExpressionList .

assign_op = [ add_op | mul_op ] “=” .
等号左边的运算数必须是可寻址的、map index 表达式或者 blank 标识符;

x = 1
*p = f()
a[i] = 23
(k) = <-ch // same as: k = <-ch
赋值运算符按两阶段执行:

等号左边的索引表达式、指针重定向,等号右边的表达式按顺序执行;

将执行结果从左向右赋值;

a, b = b, a // exchange a and b

x := []int{1, 2, 3} i := 0 i, x[i] = 1, 2 // set i = 1, x[0] = 2,等号左边的 x[i] 先执行。

i = 0 x[i], i = 2, 1 // set x[0] = 2, i = 1

x[0], x[0] = 1, 2 // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)

x[1], x[3] = 4, 5 // set x[1] = 4, then panic setting x[3] = 5.

type Point struct { x, y int } var p *Point x[2], p.x = 6, 7 // set x[2] = 6, then panic setting p.x = 7

i = 2 x = []int{3, 5, 7} for i, x[i] = range x { // set i, x[2] = 0, x[0] break } // after this loop, i == 0 and x == []int{3, 5, 3}

If statements
IfStmt = “if” [ SimpleStmt “;” ] Expression Block [ “else” ( IfStmt | Block ) ] .

if 后面可以包含初始化语句 SimpleStmt,表达式后面是 block,故必须使用大括号扩住语句;
Switch statements
Swtich 语句提供了多路径执行的方式(multi-way execution),将表达式或类型与各 Case 子句比较,从而决定执行哪一个分支。

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
Expression switches
ExprSwitchStmt = “switch” [ SimpleStmt “;” ] [ Expression ] “{” { ExprCaseClause } “}” .
ExprCaseClause = ExprSwitchCase “:” StatementList .
ExprSwitchCase = “case” ExpressionList | “default” .
如果 switch 表达式结果是无类型常量,则首先被转换为对应的缺省类型,nil 不能作为 switch expression。

如果 case 表达式是无类型的 x,则先被转换为 switch 表达式结果的类型 t,x 和 t 必须是可比较的。

各 case 的语句列表的最后一条可以是 fallthrough 语句,表示控制流转移到下一条 case 子句,否则执行完 case 子句后,将跳转到 switch 语句的结束位置(不需要使用 break 语句)。

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}

switch x := f(); { // missing switch expression means “true”
case x < 0: return -x
default: return x
}

switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
Type switches
type switch 比较的是类型而非值,它使用类型断言的形式,但是类型是关键字 type 而非实际类型。

switch x.(type) {
// cases
}
x 必须是接口类型,case 列出的类型必须是各不相同的;

TypeSwitchStmt = “switch” [ SimpleStmt “;” ] TypeSwitchGuard “{” { TypeCaseClause } “}” .
TypeSwitchGuard = [ identifier “:=” ] PrimaryExpr “.” “(” “type” “)” .
TypeCaseClause = TypeSwitchCase “:” StatementList .
TypeSwitchCase = “case” TypeList | “default” .
TypeList = Type { “,” Type } .
case 子句是 StatementList 而不是 block,所以不需要大括号括起来。

TypeSwitchGuard 可能包含一个短变量声明,相当于在每个 case 子句前面声明了一个变量,如果 case 子句的 TypeList 只包含一个类型,则该变量即为相应的类型,否则为接口类型;

switch i := x.(type) {
case nil:
printString(“x is nil”) // type of i is type of x (interface{})
case int:
printInt(i) // type of i is int
case float64:
printFloat64(i) // type of i is float64
case func(int) float64:
printFunction(i) // type of i is func(int) float64
case bool, string:
printString(“type is bool or string”) // type of i is type of x (interface{})
default:
printString(“don’t know the type”) // type of i is type of x (interface{})
}
type switch 不支持 fallthrough 语句;

For statements
ForStmt = “for” [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .

for a < b {
a *= 2
}
如果 Condition 为空,则相当于 true。

ForClause = [ InitStmt ] “;” [ Condition ] “;” [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
三个子句可以省略,但是后面的分号不能省略;

for i := 0; i < 10; i++ {
f(i)
}

for cond { S() } is the same as for ; cond ; { S() }
for { S() } is the same as for true { S() }

RangeClause = [ ExpressionList “=” | IdentifierList “:=” ] “range” Expression .
range 右边的表达式可以为 array、array 的指针、slice、string、map 和接收数据的 channel,表达式左边的运算数必须是可寻址、map index 表达式;

range 表达式只在 loop 执行前执行一次,如果表达式是 array、array 指针,且 range 左边只有一个变量,则只会执行 range 表达式结果的 length 函数;

range 左边的函数调用在迭代前只会被执行一次。

在迭代 map 时,不能保证顺序,如果 map 元素在没有迭代到前被删除,则后续不会迭代到该元素。如果在迭代过程中创建了元素,则该元素可能在后续被迭代到,也可能不会被迭代到。

range 左边的表达式列表或标识符列表可以为空:

// empty a channel
for range ch {}
for range 迭代 nil map 时立即返回。迭代 nil channel 时,将一直被阻塞。

Go statements
GoStmt = “go” Expression .
表达式必须是函数或者方法调用,丢弃返回值。

Select statements
SelectStmt = “select” “{” { CommClause } “}” .
CommClause = CommCase “:” StatementList .
CommCase = “case” ( SendStmt | RecvStmt ) | “default” .
RecvStmt = [ ExpressionList “=” | IdentifierList “:=” ] RecvExpr . // 接收语句可以定义新的变量;
RecvExpr = Expression .
case 子句是 StatementList 而不是 block,所以不需要用大括号括起来,可以为空。

select 语句的执行步骤:

按照源文件顺序执行 channel 运算数(发送和接收),发送运算符右边的运算数,且只执行一次,结果是发送或接收的 channel 和发送的数据,在此过程中可能产生副作用,但与选择哪个 case 执行无关。接收等号左边的表达式在此阶段并未执行;
如果有一个或多个通信可以处理,则随机的选择一个。否则如果有 default,则选择 default 子句,如果没有 default,则 select 阻塞直到有通信可以进行;
执行相应的通信操作(default 子句除外);
如果选择的子句包含短变量的 channel 接收数据,则执行表达式左边的表达式;
执行选择的 case 包含的语句列表;
接收等号左边的表达式只是在选中执行对应的接收子句后才会被执行。

只包含 nil channel 且无 default 子句的 select 语句会一直被阻塞。

for { // send random sequence of bits to c
select {
case c <- 0: // note: no statement, no fallthrough, no folding of cases
case c <- 1:
}
}

select {} // block forever
Return statements
ReturnStmt = “return” [ ExpressionList ] .
函数 F 的 return 语句终止函数 F 的执行,在返回 F 的 caller 前,执行在 F 内 defered 的函数,defered 的函数可以修改 F return 的值。

如果函数没有返回值, return 后面必须是空表达式列表。如果函数有返回值,则有三种方式指定 return 返回值:

return 后面跟返回值表达式列表,各表达式的值必须能赋值给函数返回值类型;
return 后面跟函数调用,该的函数返回值列表必须能赋值给 caller 的各返回值类型;
如果函数定义了命名的返回值列表,则 return 后面可以使空表达式列表;
当执行函数时,函数的返回值初始化为对应类型的 zero value,return 语句在执行 defered 函数前设置它们的值。

实现限制:如果函数内重新定义了和函数返回值同名的变量,则不能使用空 return 语句返回,否则编译器出错退出: invalid return statement: err is shadowed

Break statements
BreakStmt = “break” [ Label ] .
break 语句用于终止同函数中最内层 for、switch、select 语句的执行。如果带 label,则 label 必须紧位于 for,switch,select 语句前面:

OuterLoop:
for i = 0; i < n; i++ {
for j = 0; j < m; j++ {
switch a[i][j] {
case nil:
state = Error
break OuterLoop
case item:
state = Found
break OuterLoop
}
}
}
Continue statements
ContinueStmt = “continue” [ Label ] .
continue 用于开始执行同函数 for loop 的下一次迭代。如果带 label,则 label 必须紧位于 for 语句前面。

Goto statements
GotoStmt = “goto” Label .
goto 只能跳转到同函数的 label 位置。

goto 不能跳转到另一个不属于自己的 block,如下面代码片段是错误的:

if n%2 == 1 {
goto L1
}
for n > 0 {
f()
n–
L1:
f()
n–
}
Fallthrough statements
FallthroughStmt = “fallthrough” .
fallthrough 语句用于将 switch 语句的执行控制转移到下一个 case。

Defer statements
DeferStmt = “defer” Expression .
defer 后的函数表达式在函数返回前,或者对应的 goroutine panicking,按照 defered 的函数列表反序执行。

如果函数表达式执行结果是 nil,在 defer 执行函数的时候 runtime panic。

lock(l)
defer unlock(l) // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}

// f returns 1
func f() (result int) {
defer func() { // defer 的函数在外层函数 return 语句执行完毕,函数返回到 caller 时执行。
result++
}()
return 0
}
Built-in functions
内置函数返回值没有对应的标准 Go 类型,因此只能出现在函数调用表达式中,不能作为变量传递;

Close
向已关闭的 channel 发送数据会引起 panic;关闭 nil channel 也会引起 panic;

Length and capacity
nil slice、map、channel 的长度是 0, nil slice、channel 的容量也是 0;

Allocation
new(T)

为类型 T 分配一个存储空间,初始化为对应类型的 zero value。

Making slices, maps and channels
make 接收一个类型是 slice、map 或 channel 的类型 T,返回一个类型为 T(非 *T) 的值,内容初始化为对应类型的 zero value。

Appending to and copying slices
copy(dst, src []T) int
copy(dst []byte, src string) int
append 和 copy 只适用于 slice 类型。

append 返回的 slice 有可能不是传入的 slice(如果传入的 slice 空间不足,append 函数会返回一个新的 slice);

s0 := []int{0, 0}
s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0…) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]…) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}

var t []interface{} // t 未被初始化,但是可以用于 append;
t = append(t, 42, 3.1415, “foo”) // t == []interface{}{42, 3.1415, “foo”}

var b []byte
b = append(b, “bar”…) // append string contents b == []byte{‘b’, ‘a’, ‘r’ }
copy 将 src 中的元素 copy 到 dst 开头的位置,copy 的元素数目是 dst、src 最小长度;

Deletion of map elements
delete(m, k) // remove element m[k] from map m
从 map 中移除 key 对应的元素,如果 m 为 nil 或者不包含 k,则不会执行任何操作。

Manipulating complex numbers
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
Handling panics
func panic(interface{}) // 参数是接口类型
func recover() interface{} // 返回传入 panic 函数的参数

func protect(g func()) {
defer func() {
log.Println(“done”) // Println executes normally even if there is a panic
if x := recover(); x != nil {
log.Printf(“run time panic: %v”, x)
}
}()
log.Println(“start”)
g()
}
recover 函数返回 nil 的情况:

panic 函数的参数是 nil;
对应的 goroutine 没有 panicking;
没有直接在 defered 函数中调用recover 函数;
Bootstrapping
当前语言实现的几个内置函数:

print prints all arguments; formatting of arguments is implementation-specific println like print but prints spaces between arguments and a newline at the end

可以传入任何类型,但是必须要支持的基本类型:数值、boolean、字符串。

Packages
Go 程序是由 package 链接而成,pacakge 由一个或多个源文件组成。package 的元素可以导出(exported),然后在其它 package 中使用。

Source file organization
SourceFile = PackageClause “;” { ImportDecl “;” } { TopLevelDecl “;” } .
Package clause
PackageClause = “package” PackageName .
PackageName = identifier .
PackageName 不能是 blank 标识符。属于同一个 package 的源码文件需要位于同一个目录中。

Import declarations
import 语句表明当前 package 需要使用其它 package 中的元素。

ImportDecl = “import” ( ImportSpec | “(” { ImportSpec “;” } “)” ) .
ImportSpec = [ “.” | PackageName ] ImportPath .
ImportPath = string_lit .

Import declaration Local name of Sin

import “lib/math” math.Sin
import m “lib/math” m.Sin
import . “lib/math” Sin
使用 _ 作为 package name 导入其它 package 时,不导入任何元素,只是执行了对应 package 的初始化语句(side-effects initialization):

import _ “lib/math”
Program initialization and execution
The zero value
当创建变量时,如果没有指定初始值,则默认初始化为对应类型的 zero value。

Package initialization
package 级别的变量按照声明的顺序执行,但是在它所依赖的变量被初始化后执行。

多个文件中声明的变量的执行顺序取决于它们呈献给编译器的顺序。

package 的各源文件中可以定义多个 init 函数:

func init() { … }
如果 package 没有 import 其它 package,则先初始化 package level 的变量,然后调用各 init 函数。 如果 package import 了其它 package,则先初始化导入的 package,然后再初始化当前 package。 对多次导入的 package 只初始化一次。

package 初始化:变量初始化、调用 init 函数都是在一个 goroutine 中执行的,init 函数可能会创建其它 goroutine。

Program execution
一个程序编译和链接一个称为 main package 后生成的。

Errors
预定义的 error 类型如下:

type error interface {
Error() string
}
Run-time panics
在程序运行过程中,如果出现了 panic,则传给 panic 的参数类型是 runtime.Error,这个类型实现了上面的 error 接口:

package runtime

type Error interface {
error
// and perhaps other methods
}
System considerations
Package unsafe
编译器内置的 package unsafe,提供了低级别编程所需的基础设施:

package unsafe

type ArbitraryType int // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr
Pointer 是一个指针类型,但是 Pointer 值不能被 dereferenced。任何类型的指针或者 underlying 类型是 uintptr的类型都可以和 Pointer 相互转换:

var f float64
bits = *(*uint64)(unsafe.Pointer(&f))

type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))

var p ptr = nil
Alignof 和 Sizeof 函数接收任何类型的表达式,返回它的对齐和大小。

Offsetof 函数接收一个选择运算符 s.f,返回相对于 struct 地址的偏移量:

uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
Alignof、Offsetof 和 Sizeof 函数的返回值都是 uintptr 类型。

Size and alignment guarantees
对于数值类型,保证如下的大小:

type size in bytes

byte, uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64, float64, complex64 8
complex128 16

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值