The Go Programming Language Specification

Version of July 31, 2019

Introduction

这是Go编程语言的参考手册. 有关更多信息和其他文档,请参阅golang.org .

Go是一种通用语言,设计时考虑了系统编程. 它是强类型的并且是垃圾回收的,并且对并发编程有明确的支持. , whose properties allow efficient management of dependencies. 程序是由构建的,它们的属性可以有效地管理依赖关系.

语法紧凑且规则,可通过集成开发环境等自动工具轻松进行分析.

Notation

使用扩展Backus-Naur形式(EBNF)指定语法:

Production  = production_name "=" [ Expression ] "." .
Expression  = Alternative { "|" Alternative } .
Alternative = Term { Term } .
Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
Group       = "(" Expression ")" .
Option      = "[" Expression "]" .
Repetition  = "{" Expression "}" .

Productions是由术语和以下运算符构造的表达式,其优先级越来越高:

|   alternation
()  grouping
[]  option (0 or 1 times)
{}  repetition (0 to n times)

小写的生产名称用于标识词汇标记. 非终端在CamelCase中. 词法标记用双引号""或反引号`` .

a … b的形式表示从ab的替代字符集. 在规范中的其他地方也使用水平省略号来非正式地表示各种枚举或未进一步指定的代码段. 字符 (与这三个字符...相对)不是Go语言的标记.

Source code representation

源代码是采用UTF-8编码的Unicode文本. 文本没有规范化,因此单个重音代码点与通过组合重音和字母而构造的同一字符不同; 这些被视为两个代码点. to refer to a Unicode code point in the source text. 为简单起见,本文档将使用无条件的术语来指代源文本中的Unicode代码点.

每个代码点都是不同的. 例如,大写和小写字母是不同的字符.

实现限制:为了与其他工具兼容,编译器可能会禁止在源文本中使用NUL字符(U + 0000).

实现限制:为了与其他工具兼容,如果编译器是源文本中的第一个Unicode代码点,则可以忽略UTF-8编码的字节顺序标记(U + FEFF). 在源中的其他任何地方都可能不允许使用字节顺序标记.

Characters

以下术语用于表示特定的Unicode字符类:

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 "Number, decimal digit" */ .

Unicode标准8.0中 ,第4.5节"常规类别"定义了一组字符类别. Go将"字母"类别Lu,Ll,Lt,Lm或Lo中的任何字符视为Unicode字母,将"数字"类别Nd中的所有字符视为Unicode数字.

Letters and digits

下划线字符_ (U + 005F)被认为是字母.

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
binary_digit  = "0" | "1" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .

Lexical elements

Comments

Comments serve as program documentation. There are two forms:

  1. 以字符序列//开头,并在行末停止.
  2. 以字符序列/*开头,并以随后的第一个字符序列*/ .

注释不能在符文字符串文字或注释内部开始. 不包含换行符的一般注释就像一个空格. 任何其他注释都像换行符.

Tokens

令牌构成Go语言的词汇表. , , , and . 有四类: , , 以及 . , formed from spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and newlines (U+000A), is ignored except as it separates tokens that would otherwise combine into a single token. ,从空间(U + 0020)形成,水平突出部(U + 0009),回车(U + 000D)和换行(U + 000A),除非它分离,否则将结合成一个单一令牌被忽略令牌. 另外,换行符或文件结尾可能会触发分号的插入. 在将输入分成令牌时,下一个令牌是形成有效令牌的最长字符序列.

Semicolons

形式语法使用分号";" 作为许多作品的终结者. Go程序可以使用以下两个规则来忽略大多数分号:

  1. 当输入分解为令牌时,如果该行是令牌,则在该行的最终令牌之后立即将分号自动插入令牌流中
  2. To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".

为了反映惯用用法,本文档中的代码示例使用这些规则省略了分号.

Identifiers

标识符命名程序实体,例如变量和类型. 标识符是一个或多个字母和数字的序列. 标识符中的第一个字符必须是字母.

identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ

一些标识符是预先声明的 .

Keywords

以下关键字是保留关键字,不能用作标识符.

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 punctuation

以下字符序列表示运算符 (包括赋值运算符 )和标点符号:

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=

Integer literals

整数常量是代表整数常量的数字序列. 可选的前缀设定非十进制基数: 0b0B二进制, 00o ,或0O为八进制和0x0X为十六进制. 单个0被认为是十进制零. 在十六进制文字中,字母afAF表示值10至15.

为了便于阅读,下划线字符_可能出现在基本前缀之后或连续数字之间; 这样的下划线不会更改文字的值.

int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .

decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits  = binary_digit { [ "_" ] binary_digit } .
octal_digits   = octal_digit { [ "_" ] octal_digit } .
hex_digits     = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600       // second character is capital letter 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727

_42         // an identifier, not an integer literal
42_         // invalid: _ must separate successive digits
4__2        // invalid: only one _ at a time
0_xBadFace  // invalid: _ must separate successive digits

Floating-point literals

浮点字面是一个十进制或十六进制表示浮点常数 .

十进制浮点文字由整数部分(十进制数字),小数点,小数部分(十进制数字)和指数部分( eE后跟可选的符号和十进制数字)组成. 整数部分或小数部分之一可以被省略; 可以省略小数点之一或指数部分. 指数值exp将尾数(整数和小数部分)缩放10 exp .

十六进制浮点文字包括一个0x0X前缀,一个整数部分(十六进制数字),一个小数点,一个小数部分(十六进制数字)和一个指数部分( pP后跟一个可选的符号和十进制数字) ). 整数部分或小数部分之一可以被省略; 小数点也可以省略,但是需要指数部分. (此语法与IEEE 754-2008§5.12.3中给出的语法匹配.)指数值exp将尾数(整数和小数部分)缩放2 exp .

为了便于阅读,下划线字符_可能出现在基本前缀之后或连续数字之间; 这样的下划线不会更改文字值.

float_lit         = decimal_float_lit | hex_float_lit .

decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
                    decimal_digits decimal_exponent |
                    "." decimal_digits [ decimal_exponent ] .
decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .

hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
                    [ "_" ] hex_digits |
                    "." hex_digits .
hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (integer subtraction)

0x.p1        // invalid: mantissa has no digits
1p-2         // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
1_.5         // invalid: _ must separate successive digits
1._5         // invalid: _ must separate successive digits
1.5_e1       // invalid: _ must separate successive digits
1.5e_1       // invalid: _ must separate successive digits
1.5e1_       // invalid: _ must separate successive digits

Imaginary literals

虚构的文字表示复数常数的虚部. 它由整数浮点文字组成,后跟小写字母i . . 虚数文字的值是相应整数或浮点文字的值乘以虚数单位 .

imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .

为了向后兼容,假想文字的整数部分完全由十进制数字(可能还有下划线)组成,即使它以0开头也被视为十进制整数.

0i
0123i         // == 123i for backward-compatibility
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

Rune literals

符文文字表示符文常量 ,即标识Unicode代码点的整数值. 符文文字表示为用单引号引起的一个或多个字符,如'x''\n' . 在引号内,除换行符和未转义的单引号外,任何字符都可能出现. 用单引号引起来的字符表示字符本身的Unicode值,而以反斜杠开头的多字符序列以各种格式编码值.

最简单的形式表示引号内的单个字符; 由于Go源文本是用UTF-8编码的Unicode字符,因此多个UTF-8编码的字节可能表示单个整数值. 例如,文字'a'持有一个代表文字a字节,Unicode U + 0061,值0x61 ,而'ä'持有两个字节( 0xc3 0xa4 )代表文字a -dieresis,U + 00E4,值0xe4 .

多个反斜杠转义符允许将任意值编码为ASCII文本. 有四种方法可以将整数值表示为数字常数: \x后面紧跟两个十六进制数字; \u后面紧跟四个十六进制数字; \U后跟正好是八个十六进制数字,而普通的反斜杠\后跟正好是三个八进制数字. 在每种情况下,文字的值都是由相应基数中的数字表示的值.

尽管这些表示均产生整数,但它们具有不同的有效范围. 八进制转义符必须表示介于0和255之间(包括0和255)的值. 十六进制转义符通过构造满足此条件. 转义符\u\U表示Unicode代码点,因此其中的某些值是非法的,尤其是那些大于0x10FFFF和代理半值.

反斜杠后,某些单字符转义符表示特殊值:

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000b vertical tab
\\   U+005c backslash
\'   U+0027 single quote  (valid escape only within rune literals)
\"   U+0022 double quote  (valid escape only within string literals)

在符文文字中,所有其他以反斜杠开头的序列都是非法的.

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" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // rune literal containing single quote character
'aa'         // illegal: too many characters
'\xa'        // illegal: too few hexadecimal digits
'\0'         // illegal: too few octal digits
'\uDFFF'     // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point

String literals

字符串文字表示通过连接一系列字符获得的字符串常量 . 有两种形式:原始字符串文字和解释的字符串文字.

原始字符串文字是反引号之间的字符序列,如`foo` . 在引号内,除反引号外,任何字符都可能出现. 原始字符串文字的值是由引号之间未解释(隐式为UTF-8编码)的字符组成的字符串. 特别是,反斜杠没有特殊含义,并且字符串可能包含换行符. 原始字符串文字中的回车符('\ r')从原始字符串值中丢弃.

解释的字符串文字是双引号之间的字符序列,例如"bar" . 在引号内,除换行符和未转义的双引号外,任何字符都可能出现. ) and two-digit hexadecimal ( \x ) escapes represent individual of the resulting string; all other escapes represent the (possibly multi-byte) UTF-8 encoding of individual . Thus inside a string literal \377 and \xFF represent a single byte of value 0xFF =255, while ÿ , \u00FF , \U000000FF and \xc3\xbf represent the two bytes 0xc3 0xbf of the UTF-8 encoding of character U+00FF. 引号之间的文本构成文字的值,反斜杠转义与在符文文字中的解释相同(除非\'是非法的,而\"是合法的),但具有相同的限制.三位八进制数( \ )和两个数字的十六进制( \x 逃逸表示结果字符串的各个 ;所有其他逃逸代表(可能多字节) 单个的UTF-8编码.因此在字符串内字面\377\xFF表示值0xFF = 255的单个字节,而ÿ\u00FF\U000000FF\xc3\xbf表示字符U + 00FF的UTF-8编码的两个字节0xc3 0xbf .

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`                // same as "abc"
`\n
\n`                  // same as "\\n\n\\n"
"\n"
"\""                 // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // illegal: surrogate half
"\U00110000"         // illegal: invalid Unicode code point

这些示例都表示相同的字符串:

"日本語"                                 // UTF-8 input text
`日本語`                                 // UTF-8 input text as a raw literal
"\u65e5\u672c\u8a9e"                    // the explicit Unicode code points
"\U000065e5\U0000672c\U00008a9e"        // the explicit Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // the explicit UTF-8 bytes

如果源代码将字符表示为两个代码点,例如包含重音符号和字母的组合形式,则如果将其放置在符文文字中(不是单个代码点),结果将为错误,并且显示为如果放在字符串文字中,则两个代码点.

Constants

, , , , , and . 有 , , , , 和 . . 符文,整数,浮点数和复数常量统称为 .

常数值由符文整数浮点数虚数字符串文字表示,标识符表示常数, 常数表达式 ,结果为常数的转换或某些内置结果的值函数unsafe.Sizeof应用于任何值, caplen应用于某些表达式realimag应用于复数常量, complex应用于数值常量. 布尔真值由预先声明的常量truefalse . 预声明的标识符iota表示整数常量.

通常,复数常量是常量表达式的一种形式,将在本节中进行讨论.

数字常数表示任意精度的精确值,并且不会溢出. 因此,没有常量表示IEEE-754的负零,无穷大和非数字值.

. 常量可以是类型化或 . 文字常量truefalseiota和仅包含未类型化常量操作数的某些常量表达式是未类型化的.

常量可以通过常量声明转换来显式地指定类型,也可以在变量声明赋值中使用或在表达式中用作操作数时隐式地给定类型. 如果常量值不能表示为相应类型的值,则会出错.

which is the type to which the constant is implicitly converted in contexts where a typed value is required, for instance, in a short variable declaration such as i := 0 where there is no explicit type. 未类型的常量具有 ,该是在要求使用类型值的上下文中(例如,在简短的变量声明中,例如i := 0且没有显式类型的情况下)将常量隐式转换为该类型的类型. 无类型常量的默认类型分别是boolruneintfloat64complex128string ,这取决于它是布尔,符文,整数,浮点数,复杂还是字符串常量.

实现限制:尽管数字常量在语言中具有任意精度,但编译器可能会使用内部表示形式来实现它们,而精度有限. 也就是说,每个实现都必须:

这些要求既适用于文字常量,也适用于评估常量表达式的结果.

Variables

. 变量是用于保存的存储位置. . 允许值的集合由变量的 .

对于函数参数和结果, 变量声明函数声明函数文字的签名为命名变量保留存储空间. 调用内置函数new或获取复合文字的地址会在运行时为变量分配存储空间. 此类匿名变量是通过(可能是隐式的) 指针间接指向的 .

variables of array , slice , and struct types have elements and fields that may be addressed individually. arrayslicestruct类型的变量具有可以单独处理的元素和字段. 每个这样的元素就像一个变量.

(or just ) of a variable is the type given in its declaration, the type provided in the new call or composite literal, or the type of an element of a structured variable. 变量的 (或只是 )是其声明中给出的类型, new调用或复合文字中提供的类型或结构化变量的元素的类型. , which is the concrete type of the value assigned to the variable at run time (unless the value is the predeclared identifier nil , which has no type). 接口类型的变量还具有独特的 ,这是在运行时分配给变量的值的具体类型(除非该值是预先声明的标识符nil ,它没有类型). 动态类型在执行期间可能会有所不同,但是存储在接口变量中的值始终可分配给变量的静态类型.

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

通过引用表达式中的变量来检索变量的值; 它是分配给变量的最新值. 如果尚未为变量分配值,则其值为该类型的零值 .

Types

类型确定一组值以及特定于那些值的操作和方法. , if it has one, or specified using a , which composes a type from existing types. 一个类型可以用一个来表示,如果有一个,或者可以使用指定,该由现有类型组成.

Type      = TypeName | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
	    SliceType | MapType | ChannelType .

该语言预先声明了某些类型名称. 其他类型则带有类型声明 . —array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals. 可以使用类型文字构造数组,结构,指针,函数,接口,切片,映射和通道类型).

: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. 每个类型T都有一个 :如果T是预先声明的布尔,数字或字符串类型之一,或者是类型文字,则相应的基础类型就是T本身. 否则, T的基础类型是T在其类型声明中引用的类型的基础类型.

type (
	A1 = string
	A2 = A1
)

type (
	B1 string
	B2 B1
	B3 []B1
	B4 B3
)

string的基本类型A1A2B1B2string . []B1B3B4的基础类型是[]B1 .

Method sets

associated with it. 类型可能具有与之关联的 . 接口类型的方法集是其接口. 其他任何类型T的方法集都包含以接收者类型T声明的所有方法 . 对应的指针类型 *T的方法集是使用接收者*TT声明的所有方法的集合(也就是说,它还包含T的方法集). 进一步的规则适用于包含嵌入式字段的结构,如有关struct type的部分中所述. 其他任何类型的方法集都为空. 在方法集中,每个方法必须具有唯一的空白 方法名称 .

类型的方法集确定该类型实现的接口以及可以使用该类型的接收器调用的方法.

Boolean types

represents the set of Boolean truth values denoted by the predeclared constants true and false . 表示一组布尔真值,它们由预先声明的常量truefalse . 预定义的布尔类型为bool ; 它是定义的类型 .

Numeric types

represents sets of integer or floating-point values. 表示整数或浮点值的集合. 预先声明的与体系结构无关的数字类型是:

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

位整数的值是位宽,并使用二进制补码表示.

还有一组预先声明的数字类型,具有特定于实现的大小:

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

为了避免可移植性问题,所有数字类型均定义为类型 ,因此除byteuint8别名)runeint32的别名)外,它们都是不同的. 在表达式或赋值中混合使用不同的数字类型时,需要进行显式转换. 例如,即使int32int在特定体系结构上可能具有相同的大小,它们也不是同一类型.

String types

represents the set of string values. 表示字符串值的集合. 字符串值是一个字节序列(可能为空). 字节数称为字符串的长度,绝不能为负. 字符串是不可变的:一旦创建,就无法更改字符串的内容. 预先声明的字符串类型为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寻址元素. 数组类型始终是一维的,但可以组成多维类型.

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

Slice types

and provides access to a numbered sequence of elements from that array. 切片是连续段的描述符,并提供对该数组中元素编号序列的访问. 切片类型表示其元素类型的所有数组切片的集合. 元素的数量称为切片的长度,绝不为负. 未初始化的切片的值为nil .

SliceType = "[" "]" ElementType .

切片s长度s可以通过内置函数len ; 与数组不同,它可能在执行期间发生变化. 可以通过整数索引 0到len(s)-1寻址元素. 给定元素的切片索引可能小于基础数组中相同元素的索引.

切片一旦初始化,便始终与包含其元素的基础数组关联. 因此,一个片与其阵列以及同一阵列的其他片共享存储. 相反,不同的数组始终代表不同的存储.

切片下面的数组可能会延伸到切片的末尾. 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 new one from the original slice. 可以通过从原始切片中一个新来创建长度达到该容量的切片. 可以使用内置功能cap(a)发现切片a的容量.

使用内置函数make ,为给定​​元素类型T了一个新的初始化切片值,该函数采用切片类型和参数来指定长度和容量(可选). 使用make创建的切片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 .
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
}

如果xf是表示该字段或方法f的合法选择器 ,则将结构x嵌入字段的字段或方法 f称为 .

提升的字段的作用类似于结构的普通字段,只是它们不能用作结构的复合文字中的字段名称.

给定一个结构类型S和一个定义的类型 T ,在该方法的方法集中包括了以下改进的方法:

, which becomes an attribute for all the fields in the corresponding field declaration. 字段声明后可以跟一个可选的字符串文字 ,该成为相应字段声明中所有字段的属性. 空标签字符串等效于缺少标签. 通过反射接口使标签可见,并参与结构的类型标识 ,但否则将被忽略.

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"`
}

Pointer types

of the pointer. 指针类型表示指向给定类型的变量的所有指针的集合,称为指针的 . 未初始化指针的值为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 .

在参数或结果的列表中,名称(IdentifierList)必须全部存在或不存在. 如果存在,则每个名称代表指定类型的一项(参数或结果),并且签名中的所有非空白名称必须唯一 . 如果不存在,则每种类型都代表该类型的一项. 参数和结果列表始终带有括号,但如果有一个未命名的结果,则可以将其写为非括号类型.

函数签名中的最终传入参数可以具有以...为前缀的类型. and may be invoked with zero or more arguments for that parameter. 具有此类参数的函数称为参数( 并且可以使用零个或多个该参数的参数来调用该函数.

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" "{" { MethodSpec ";" } "}" .
MethodSpec         = MethodName Signature | InterfaceTypeName .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

与所有方法集一样,在接口类型中,每个方法必须具有唯一的空白名称.

// 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
}

可以实现一种以上的接口. 例如,如果两个类型S1S2具有方法集

func (p T) Read(p []byte) (n int, err error)   { return … }
func (p T) Write(p []byte) (n int, err error)  { return … }
func (p T) Close() error                       { return … }

(其中T代表S1S2 ),则File接口由S1S2 ,而与S1S2可能具有或共享的其他方法无关.

类型实现包含其方法的任何子集的任何接口,因此可以实现几个不同的接口. : 例如,所有类型都实现 :

interface{}

同样,考虑一下这个接口规范,该规范出现在类型声明中,以定义一个称为Locker的接口:

type Locker interface {
	Lock()
	Unlock()
}

如果S1S2也实施

func (p T) Lock() { … }
func (p T) Unlock() { … }

它们实现了Locker接口以及File接口.

接口T可以使用(可能是合格的)接口类型名称E代替方法规范. interface E in T ; 这称为T 接口E ; 它将E所有(导出和未导出)方法添加到接口T

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
}

接口类型T可能不会自己嵌入,也可能不会递归地嵌入T任何接口类型.

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

// illegal: Bad1 cannot embed itself using Bad2
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

Map types

of another type, called the key type. 映射是一种无序的一组元素,称为元素类型,由一组另一种类型的唯一 (称为键类型)索引. 未初始化映射的值为nil .

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

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

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

地图元素的数量称为其长度. 对于映射m ,可以使用内置函数len来发现它,并且在执行过程中可能会更改. 元素可以在执行期间使用赋值添加,并用索引表达式检索; 可以使用内置的delete功能delete它们.

使用内置函数make一个新的空地图值,该函数将地图类型和可选的容量提示作为参数:

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

初始容量不受其大小限制:除了nil映射外,映射的增长是为了容纳其中存储的项目数量. nil映射等效于空映射,不同之处在于不能添加任何元素.

Channel types

通道提供了一种机制,用于通过发送接收指定元素类型的值来并发执行功能以进行通信. 未初始化通道的值为nil .

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

, or . 可选的<-运算符指定通道 ,即或 . . 如果没有给出方向,则该信道是 . 通道只能通过分配或显式转换来限制发送或仅接收.

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

The <- operator associates with the leftmost chan possible:

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关闭通道. 接收操作符的多值分配形式报告在关闭通道之前是否发送了接收值.

任意数量的goroutine可以在send语句receive操作和对内置函数caplen的调用中使用单个通道,而无需进一步同步. 通道充当先进先出队列. 例如,如果一个goroutine在通道上发送值,而第二个goroutine接收它们,则按发送顺序接收值.

Properties of types and values

Type identity

两种类型或 .

定义的类型始终与任何其他类型不同. 否则,如果两种类型的基础类型文字在结构上相等,则它们是相同的; 也就是说,它们具有相同的文字结构,而相应的组件具有相同的类型. 详细地:

给出声明

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

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
)

type	C0 = B0

这些类型是相同的:

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

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

B0B1是不同的,因为它们是由不同的类型定义创建的新类型; func(int, float64) *B0func(x int, y float64) *[]string不同,因为B0[]string不同.

Assignability

如果满足以下条件之一,则可以将值x 给类型T变量 (" x可以分配给T "):

Representability

如果满足以下条件之一,则常量 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

Blocks

是匹配括号中声明和语句的可能空序列.

Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

除了源代码中的显式块,还有隐式块:

  1. 包含所有Go源文本.
  2. 每个都有一个其中包含该的所有Go源文本.
  3. 每个文件都有一个其中包含该中的所有Go源文本.
  4. 每个" if"" for"" switch"语句都被视为位于其自己的隐式块中.
  5. " switch"" select"语句中的每个子句都充当隐式块.

阻止嵌套并影响作用域 .

Declarations and scope

binds a non- blank identifier to a constant , type , variable , function , label , or package . 将非空白标识符绑定到常量类型变量函数标签 . 程序中的每个标识符都必须声明. 不能在同一块中声明两次标识符,也不能在文件和包块中声明任何标识符.

空白标识符可以像声明中的任何其他标识符一样使用,但是它不会引入绑定,因此不会被声明. 在package块中,标识符init只能用于init函数声明,并且像空白标识符一样,它不会引入新的绑定.

Declaration   = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .

的中声明标识符是其中标识表示指定的常数,类型,变量,函数,标签或包源文本的程度.

Go的词法作用域是使用block

  1. 预定义标识符的范围是Universe块.
  2. 表示在顶层(任何函数之外)声明的常量,类型,变量或函数(但不是方法)的标识符的范围是package块.
  3. 导入的软件包的软件包名称的范围是包含导入声明的文件的文件块.
  4. 表示方法接收者,函数参数或结果变量的标识符的范围是函数主体.
  5. 在函数内部声明的常量或变量标识符的范围始于ConstSpec或VarSpec的末尾(对于简短变量声明,为ShortVarDecl),并终止于最里面的包含块的末尾.
  6. 在函数内部声明的类型标识符的范围从TypeSpec中的标识符开始,到最里面的包含块的结尾结束.

可以在内部块中重新声明在块中声明的标识符. 内部声明的标识符在范围内,它表示内部声明声明的实体.

package子句不是声明. 程序包名称不会出现在任何范围内. 其目的是识别属于同一软件包的文件,并为导入声明指定默认的软件包名称.

Label scopes

标签由带标签的语句声明 ,并在" break"" continue"" goto"语句中使用. 定义从未使用过的标签是非法的. 与其他标识符相反,标签不是块作用域的,并且不会与不是标签的标识符冲突. 标签的范围是在其中声明标签的函数的主体,并且不包括任何嵌套函数的主体.

Blank identifier

is represented by the underscore character _ . 由下划线字符_ . 它用作匿名占位符,而不是常规(非空白)标识符,并且在声明操作数赋值中具有特殊含义.

Predeclared identifiers

以下标识符在Universe块中隐式声明:

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

An identifier may be exported to permit access to it from another package. An identifier is exported if both:

  1. 标识符名称的第一个字符是Unicode大写字母(Unicode类" Lu"); 和
  2. 标识符在包块中声明,或者是字段名方法名 .

所有其他标识符都不会导出.

Uniqueness of identifiers

if it is from every other in the set. 给定一组标识符,如果是从每一个集合的标识符被称为 . 如果两个标识符的拼写不同,或者它们出现在不同的程序包中且未导出 ,则它们是不同的. 否则,它们是相同的.

Constant declarations

A constant declaration binds a list of identifiers (the names of the constants) to the values of a list of constant expressions. The number of identifiers must be equal to the number of expressions, and the nth identifier on the left is bound to the value of the nth expression on the right.

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

IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

如果存在类型,则所有常量都采用指定的类型,并且表达式必须可分配给该类型. 如果省略类型,则常量采用相应表达式的各个类型. 如果表达式值是未类型化的常量 ,则声明的常量将保持未类型化,并且常量标识符表示常量值. 例如,如果表达式是浮点文字,则常量标识符表示浮点常数,即使文字的小数部分为零也是如此.

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声明列表中,表达式列表可以从第一个ConstSpec中省略. 这样的空列表等效于第一个前面的非空表达式列表及其类型(如果有)的文本替换. 因此,省略表达式列表等同于重复前面的列表. 标识符的数量必须等于上一个列表中的表达式的数量. 与iota常量生成器一起,此机制允许轻量级声明连续值:

const (
	Sunday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Partyday
	numberOfDays  // this constant is not exported
)

Iota

常量声明中 ,预声明的标识符iota表示连续的无类型整数常量 . 它的值是该常量声明中相应ConstSpec的索引,从零开始. 它可以用来构造一组相关的常量:

const (
	c0 = iota  // c0 == 0
	c1 = iota  // c1 == 1
	c2 = iota  // c2 == 2
)

const (
	a = 1 << iota  // a == 1  (iota == 0)
	b = 1 << iota  // b == 2  (iota == 1)
	c = 3          // c == 3  (iota == 2, unused)
	d = 1 << iota  // d == 8  (iota == 3)
)

const (
	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)
)

const x = iota  // x == 0
const y = iota  // y == 0

根据定义,在同一个ConstSpec中多次使用iota都具有相同的值:

const (
	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
	_, _                                  //                        (iota == 2, unused)
	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

最后一个示例利用了最后一个非空表达式列表的隐式重复 .

Type declarations

, to a type . 类型声明将标识符( 绑定到type . 类型声明有两种形式:别名声明和类型定义.

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

A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.

TypeDef = identifier Type .

The new type is called a 定义类型. It is different from any other type, including the type it is created from.

type (
	Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types
	polar Point                   // polar and Point denote different types
)

type TreeNode struct {
	left, right *TreeNode
	value *Comparable
}

type Block interface {
	BlockSize() int
	Encrypt(src, dst []byte)
	Decrypt(src, dst []byte)
}

定义的类型可能具有与之关联的方法 . 它不继承绑定到给定类型的任何方法,但是接口类型或复合类型的元素的方法集保持不变:

// 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 PtrMutex's underlying type *Mutex 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 embedded field Mutex.
type PrintableMutex struct {
	Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

类型定义可用于定义不同的布尔,数字或字符串类型,并将方法与它们相关联:

type TimeZone int

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

func (tz TimeZone) String() string {
	return fmt.Sprintf("GMT%+dh", tz)
}

Variable declarations

变量声明创建一个或多个变量 ,将相应的标识符绑定到它们,并为每个变量赋予一个类型和一个初始值.

VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
	i       int
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name]  // map lookup; only interested in "found"

如果给出了一个表达式列表,则使用赋值规则中的表达式对变量进行初始化. 否则,将每个变量初始化为其零值 .

如果存在类型,则为每个变量指定该类型. 否则,将为每个变量分配赋值的相应初始化值的类型. 如果该值是未类型化的常量,则首先将其隐式转换为其默认类型 ; 如果它是无类型的布尔值,则首先将其隐式转换为bool类型. 预声明的值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

实现限制:如果从未使用过变量,则编译器可能会在函数体内声明一个变量为非法.

Short variable declarations

使用以下语法:

ShortVarDecl = IdentifierList ":=" ExpressionList .

它是带有初始化表达式但没有类型的常规变量声明的简写:

"var" IdentifierList = ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate

variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non- blank variables is new. 与常规变量声明不同,短变量声明可以变量,前提是它们最初是在相同类型的同一个块中(如果该块是函数体,则在参数列表中)早先声明过,并且具有至少一个非变量是新的. 因此,重新声明只能出现在多变量简短声明中. 重新声明不会引入新变量; 它只是为原始值分配一个新值.

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"语句的初始化程序,它们可用于声明局部临时变量.

Function declarations

函数声明将标识符( 绑定到函数.

FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

如果函数的签名声明了结果参数,则函数主体的语句列表必须以终止语句结尾 .

func IndexRune(s string, r rune) int {
	for i, c := range s {
		if c == r {
			return i
		}
	}
	// invalid: missing return statement
}

函数声明可以省略主体. 这样的声明为Go外部实现的功能(例如汇编例程)提供了签名.

func min(x int, y int) int {
	if x < y {
		return x
	}
	return y
}

func flushICache(begin, end uintptr)  // implemented externally

Method declarations

. 方法是具有的功能 . , to a method, and associates the method with the receiver's . 方法声明将标识符, 绑定到方法,并将该方法与接收者的相关联.

MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

接收方是通过方法名称前面的附加参数部分指定的. 该参数部分必须声明一个非可变参数,即接收器. 其类型必须是一个定义的类型T或一个指向定义类型T . . T被称为接收方 . 接收方基本类型不能是指针或接口类型,并且必须在与方法相同的程序包中定义. to its receiver base type and the method name is visible only within selectors for type T or *T . 据说该方法已到其接收方基本类型,并且该方法名称仅在类型T*T 选择器中可见.

空白的接收者标识符在方法签名中必须唯一 . 如果没有在方法主体内部引用接收方的值,则可以在声明中省略其标识符. 通常,这同样适用于功能和方法的参数.

对于基本类型,绑定到它的方法的非空白名称必须是唯一的. 如果基本类型是struct type ,则非空白方法和字段名称必须不同.

给定定义的类型Point ,声明

func (p *Point) Length() float64 {
	return math.Sqrt(p.x * p.x + p.y * p.y)
}

func (p *Point) Scale(factor float64) {
	p.x *= factor
	p.y *= factor
}

将接收器类型*Point LengthScale方法绑定到基本类型Point .

方法的类型是将接收方作为第一个参数的函数的类型. 例如,方法Scale具有类型

func(p *Point, factor float64)

但是,以这种方式声明的函数不是方法.

Expressions

表达式通过将运算符和函数应用于操作数来指定值的计算.

Operands

操作数表示表达式中的基本值. 操作数可以是文字,表示常量变量函数的(可能是限定的 )非空白标识符,或者是带括号的表达式.

空白标识符只能作为操作数出现在赋值的左侧.

Operand     = Literal | OperandName | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent.

Qualified identifiers

合格标识符是具有包名前缀的合格标识符. 软件包名称和标识符都不能为 .

QualifiedIdent = PackageName "." identifier .

合格标识符访问另一个包中的标识符,该标识符必须被导入 . 标识符必须在该包的包块导出并声明.

math.Sin	// denotes the Sin function in package math

Composite literals

复合文字量为结构,数组,切片和映射构造值,并在每次对其求值时创建一个新值. 它们由文字的类型和紧随其后的元素列表组成. 每个元素可以可选地在对应的关键字之后.

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 .

LiteralType的基础类型必须是struct,array,slice或map类型(语法强制执行此约束,除非将类型指定为TypeName). 元素和键的类型必须可分配给文字类型的相应字段,元素和键类型; 没有其他转换. 键被解释为结构文字的字段名,数组和切片文字的索引以及映射文字的键. 对于地图文字,所有元素都必须具有键. 指定具有相同字段名称或常数键值的多个元素是错误的. 对于非恒定映射键,请参阅评估顺序部分.

对于结构文字,以下规则适用:

给出声明

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

一个人可以写

origin := Point3D{}                            // zero value for Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x

对于数组和切片文字,以下规则适用:

获取复合文字的地址会生成一个指向使用文字的值初始化的唯一变量的指针.

var pointer *Point3D = &Point3D{y: 1000}

请注意,切片或图类型的零值与相同类型的已初始化但空值不同. 因此,获取空切片或映射复合文字的地址与使用new分配新切片或映射值的效果不同.

p1 := &[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0
p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0

数组文字的长度是在文字类型中指定的长度. 如果文字中提供的元素少于长度,则缺少的元素将被设置为数组元素类型的零值. 为元素提供的索引值超出数组的索引范围是错误的. 符号...指定的数组长度等于最大元素索引加一.

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

切片文字描述了整个基础数组文字. 因此,切片文字的长度和容量为最大元素索引加一. 切片文字的格式为

[]T{x1, x2, … xn}

并且是应用于数组的切片操作的简写:

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

在数组T ,切片或映射类型T的复合文字中,如果元素或映射键本身与T的元素或键类型相同,则本身是复合文字的元素或映射键可能会忽略相应的文字类型. 同样,当元素或键的类型为*T时,作为组合文字的地址的元素或键可能会忽略&T *T .

[...]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}}
map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}

type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}

当使用LiteralType的TypeName形式的复合文字作为关键字和" if"," for"或" switch"语句的块的开头括号之间的操作数出现时,就会产生解析歧义.不能用括号,方括号或花括号括起来. 在这种罕见的情况下,字面量的开头括号被错误地解析为引入语句块的括号. 要解决歧义,复合文字必须出现在括号内.

if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }

有效数组,切片和映射文字的示例:

// list of prime numbers
primes := []int{2, 3, 5, 7, 9, 2147483647}

// vowels[ch] is true if ch is a vowel
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// 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}

// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
noteFrequency := map[string]float32{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

Function literals

函数文字代表一个匿名函数 .

FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b < int(z) }

可以将函数文字分配给变量或直接调用.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

: they may refer to variables defined in a surrounding function. 函数文字是 :它们可以引用在周围函数中定义的变量. 然后,这些变量在周围的函数和函数文字之间共享,并且只要可以访问它们就可以保留.

Primary expressions

主表达式是一元和二进制表达式的操作数.

PrimaryExpr =
	Operand |
	Conversion |
	MethodExpr |
	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 ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()

Selectors

对于不是包名主表达式 x

x.f

表示值为x的字段或方法f (或有时为*x ;请参见下文). ; 标识符f称为(字段或方法) ; 它不能是空白标识符 . 选择器表达式的类型是f的类型. 如果x是程序包名称,请参阅有关合格标识符的部分.

选择器f可以表示类型T的字段或方法f ,或者可以引用T的嵌套嵌入字段的字段或方法f . in T . 遍历到达f的嵌入场的数量称为其在T . 在T声明的字段或方法f的深度为零. 在T嵌入字段A中声明的字段或方法f的深度是Af的深度加一.

以下规则适用于选择器:

  1. 对于类型T*T的值x (其中T不是指针或接口类型), xf表示存在f T中最浅深度的字段或方法. 如果不存在一个最浅的f ,则选择器表达式是非法的.
  2. 对于值x型的I其中I是一个接口类型, xf表示具有名称的实际方法f的动态值的x . 如果I方法集中没有名称为f方法 ,则选择器表达式无效.
  3. 作为例外,如果x的类型是已定义的指针类型,而(*x).f是表示字段(但不是方法)的有效选择器表达式,则xf(*x).f简写.
  4. 在所有其他情况下, xf都是非法的.
  5. 如果x是指针类型,并且值为nil并且xf表示结构字段,则分配或评估xf会导致运行时恐慌 .
  6. 如果x是接口类型,并且值为nil ,则调用评估方法xf会导致运行时恐慌 .

例如,给定声明:

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     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil
var q Q = p

一个人可以这样写:

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

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

但以下无效:

q.M0()       // (*q).M0 is valid but not a field selector

Method expressions

如果M在类型T方法集中 ,则TM是可以作为常规函数调用的函数,该函数具有与M相同的参数,并以作为方法接收方的附加参数作为前缀.

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = Type .

考虑具有两种方法的结构类型TMv ,其接收者为T类型,以及Mp ,其接收者为*T类型.

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

产生一个与Mv等效的函数,但将显式接收器作为第一个参数; 它有签名

func(tv T, a int) int

通常可以通过显式接收器调用该函数,因此这五个调用是等效的:

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

产生一个表示签名的Mp的函数值

func(tp *T, f float32) float32

对于带有值接收器的方法,可以使用显式指针接收器派生一个函数,因此

(*T).Mv

产生代表签名的Mv的函数值

func(tv *T, a int) int

这样的函数通过接收器间接创建一个值,该值作为接收器传递给基础方法. 该方法不会覆盖在函数调用中传递其地址的值.

最后一种情况是指针接收器方法的值接收器函数,这是非法的,因为指针接收器方法不在值类型的方法集中.

从方法派生的函数值使用函数调用语法进行调用; 提供接收方作为呼叫的第一个参数. 也就是说,给定f := T.Mvf作为f(t, 7)而不是tf(7)调用. 要构造绑定接收者的函数 ,请使用函数文字方法value .

从接口类型的方法派生函数值是合法的. 结果函数采用该接口类型的显式接收器.

Method values

. 如果表达式x具有静态类型TM是在方法集合类型的TxM称为 . 方法值xM是可以使用与xM的方法调用相同的参数调用的函数值. 在计算方法值期间对表达式x进行评估并保存; 然后,保存的副本将在任何调用中用作接收者,以后可能会执行.

类型T可以是接口类型,也可以是非接口类型.

就像在上面的方法表达式的讨论中一样,考虑具有两种方法的结构类型TMv ,其接收者是T类型,以及Mp ,其接收者是*T类型.

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

这两个调用是等效的:

t.Mv(7)
f := t.Mv; f(7)

同样,表达式

pt.Mp

产生一个类型的函数值

func(float32) float32

选择器一样 ,使用指针使用值接收器对非接口方法的引用将自动取消引用该指针: pt.Mv等效于(*pt).Mv .

方法调用一样 ,使用可寻址值的指针接收器对非接口方法的引用将自动采用该值的地址: t.Mp等效于(&t).Mp .

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由索引x . or , respectively. 值x分别称为或 . 适用以下规则:

如果a不是地图:

For a of array type A:

a指针数组类型:

For a of slice type S:

For a of string type:

For a of map type M:

否则a[x]是非法的.

map[K]V类型的映射a上的索引表达式,用于特殊形式的赋值或初始化

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]

产生另一个无类型的布尔值. 如果键x存在于映射中,则ok值为true ,否则为false .

分配给nil映射的元素会导致运行时恐慌 .

Slice expressions

切片表达式根据字符串,数组,指向数组或切片的指针构造子字符串或切片. 有两种变体:一种简单的形式,它指定一个上限和一个下限;一个完整的形式,它还指定一个容量上的界限.

Simple slice expressions

对于字符串,数组,指向数组的指针或切片a ,主表达式

a[low : high]

构造一个子字符串或切片. low and high select which elements of operand a appear in the result. lowhigh选择的哪些元素的操作数a出现在结果中. 结果的索引从0开始,长度等于high - low . 切片阵列后a

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

切片s具有[]int类型,长度3,容量4和元素

s[0] == 2
s[1] == 3
s[2] == 4

For convenience, any of the indices may be omitted. A missing low index defaults to zero; a missing high index defaults to the length of the sliced operand:

a[2:]  // same as a[2 : len(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : len(a)]

如果a是指向数组的指针,则a[low : high](*a)[low : high]简写.

if 0 <= low <= high <= len(a) , otherwise they are . 对于数组或字符串,如果0 <= low <= high <= len(a) ,则索引 ,否则它们 . 对于切片,索引的上限是切片容量cap(a)而不是长度. 常数索引必须是非负数,并且可以由int类型的值表示 ; 对于数组或常量字符串,常量索引也必须在范围内. 如果两个索引都恒定,则它们必须满足low <= high . 如果索引在运行时超出范围,则会发生运行时恐慌 .

除未类型化的字符串外 ,如果切片的操作数是字符串或切片,则切片操作的结果是与该操作数相同类型的非常数值. 对于无类型字符串操作数的结果是类型的非恒定的值string . 如果切片的操作数是数组,则它必须是可寻址的,并且切片操作的结果是与数组具有相同元素类型的切片.

如果有效切片表达式的切片操作数是nil slice,则结果是nil slice. 否则,如果结果是切片,则它将与操作数共享其基础数组.

var a [10]int
s1 := a[3:7]   // underlying array of s1 is array a; &s1[2] == &a[5]
s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element

Full slice expressions

对于数组,指向数组的指针或切片a (但不包括字符串),则是主表达式

a[low : high : max]

构造与简单切片表达式a[low : high]相同类型,长度和元素相同的切片. 此外,它通过将切片设置为max - low来控制切片的容量. 仅第一个索引可以省略; 它的默认值为0切片阵列后a

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

切片t具有[]int类型,长度2,容量4和元素

t[0] == 2
t[1] == 3

对于简单的切片表达式,如果a是指向数组的指针,则a[low : high : max](*a)[low : high : max]简写. 如果切片的操作数是一个数组,则它必须是可寻址的 .

if 0 <= low <= high <= max <= cap(a) , otherwise they are . 如果0 <= low <= high <= max <= cap(a) ,则索引 ,否则它们 . 常数索引必须是非负数,并且可以由int类型的值表示 ; 对于数组,常量索引也必须在范围内. 如果多个索引为常数,则存在的常数必须在相对范围内. 如果索引在运行时超出范围,则会发生运行时恐慌 .

Type assertions

对于具有接口类型和类型T的表达式x ,主表达式

x.(T)

断言x不是nil并且x存储的值是T类型. . 符号x.(T)称为 .

更确切地说,如果T不是接口类型,则x.(T)断言x的动态类型 T 相同 . 在这种情况下, T必须实现 x的(接口)类型; 否则类型断言无效,因为x不可能存储类型T的值. 如果T是接口类型,则x.(T)断言x的动态类型实现了接口T

如果类型断言成立,则表达式的值为存储在x的值,其类型为T 如果类型断言为假,则会发生运行时恐慌 . 换句话说,即使x的动态类型仅在运行时才知道,但在正确的程序中x.(T)的类型还是T

var x interface{} = 7          // x has dynamic type int and value 7
i := x.(int)                   // i has type int and value 7

type I interface { m() }

func f(y I) {
	s := y.(string)        // illegal: string does not implement I (missing method m)
	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
	…
}

在特殊形式的赋值或初始化中使用的类型断言

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok T1 = x.(T)

产生另一个无类型的布尔值. 如果断言成立,则ok值为true . 否则为false并且v值为 T类型的零值 . 在这种情况下,不会发生运行时恐慌 .

Calls

给定函数类型F的表达式f

f(a1, a2, … an)

用参数a1, a2, … an调用f . 除一种特殊情况外,参数必须是可分配F参数类型的单值表达式,并在调用函数之前对其求值. 表达式的类型是F的结果类型. 方法调用是类似的,但是方法本身根据该方法的接收器类型的值指定为选择器.

math.Atan2(x, y)  // function call
var pt *Point
pt.Scale(3.5)     // method call with receiver pt

在函数调用中,函数值和参数按通常的顺序求值. 对它们进行评估之后,调用的参数将按值传递给函数,并且被调用函数开始执行. 当函数返回时,该函数的返回参数按值传递回调用函数.

调用nil函数值会导致运行时panic .

)) will invoke f after binding the return values of g to the parameters of f in order. 作为一种特殊情况,如果一个函数或方法g的返回值数量相等并且可以分别分配给另一个函数或方法f的参数,则调用f(g( ))将绑定返回值后将调用f gf的参数顺序. f的调用除g ,不得包含任何其他参数,并且g必须至少具有一个返回值. 如果f具有final ...参数,则会为其分配g的返回值,这些返回值在分配常规参数后仍保留.

func Split(s string, pos int) (string, string) {
	return s[0:pos], s[pos:]
}

func Join(s, t string) string {
	return s + t
}

if Join(Split(value, len(value)/2)) != value {
	log.Panic("test fails")
}

如果x方法集包含m并且参数列表可以分配给m的参数列表,则方法调用xm()是有效的. 如果x可寻址的并且&x的方法集包含m ,则xm()(&x).m()简写:

var p Point
p.Scale(3.5)

没有独特的方法类型,也没有方法文字.

Passing arguments to ... parameters

如果f是最终参数p...T类型的可变参数,则在fp的类型等效于[]T类型. 如果在没有p实际参数的情况下调用f ,则传递给p值为nil . 否则,传递的值是[]T类型的新切片,具有新的基础数组,其连续元素是实际参数,所有参数都必须可分配T 因此,切片的长度和容量是绑定到p的参数的数量,并且对于每个调用站点可能有所不同.

给定功能和调用

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

Greetingwho就会拥有价值nil的第一个电话,和[]string{"Joe", "Anna", "Eileen"}在第二位.

如果最后一个参数可分配给切片类型[]T ,则在参数后跟... ,将其不变地作为...T参数的值传递. 在这种情况下,不会创建新的切片.

由于片s和呼叫

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

Greetingwho将具有相同的值作为s与相同的基本阵列.

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   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

比较在其他地方讨论. 对于其他二进制运算符,除非该操作涉及shifts或未类型化的常量,否则操作数类型必须相同 . 对于仅涉及常量的操作,请参见关于常量表达式的部分.

除移位操作外,如果一个操作数是未类型化的常量 ,而另一个操作数不是,则该常量将隐式转换为另一种操作数的类型.

移位表达式中的右操作数必须具有整数类型,或者是可由uint类型的值表示的无类型常量. 如果非恒定移位表达式的左操作数是未类型化的常数,则首先将其隐式转换为如果仅将移位表达式替换为其左操作数时将假定的类型.

var s uint = 33
var i = 1<<s                  // 1 has type int
var j int32 = 1<<s            // 1 has type int32; j == 0
var k = uint64(1<<s)          // 1 has type uint64; k == 1<<33
var m int = 1.0<<s            // 1.0 has type int; m == 0 if ints are 32bits in size
var n = 1.0<<s == j           // 1.0 has type int32; n == true
var o = 1<<s == 2<<s          // 1 and 2 have type int; o == true if ints are 32bits in size
var p = 1<<s == 1<<33         // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
var u = 1.0<<s                // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0          // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0          // illegal: 1 has type float64, cannot shift
var v float32 = 1<<s          // illegal: 1 has type float32, cannot shift
var w int64 = 1.0<<33         // 1.0<<33 is a constant shift expression
var x = a[1.0<<s]             // 1.0 has type int; x == a[0] if ints are 32bits in size
var a = make([]byte, 1.0<<s)  // 1.0 has type int; len(a) == 0 if ints are 32bits in size

Operator precedence

一元运算符的优先级最高. 由于++--运算符形成语句而不是表达式,因此它们不在运算符层次结构之内. 结果,语句*p++(*p)++ .

二进制运算符有五个优先级. 乘法运算符绑定最强,其次是加法运算符,比较运算符, && (逻辑与),最后是|| (逻辑或):

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

具有相同优先级的二进制运算符从左到右关联. 例如, x / y * z(x / y) * z .

+x
23 + 3*x[i]
x <= f()
^a >> b
f() || g()
x == y+1 && <-chanPtr > 0

Arithmetic operators

算术运算符应用于数值,并产生与第一个操作数相同类型的结果. 四个标准算术运算符( +-*/ )适用于整数,浮点数和复数类型; +也适用于字符串. 按位逻辑和移位运算符仅适用于整数.

+    sum                    integers, floats, complex values, strings
-    difference             integers, floats, complex values
*    product                integers, floats, complex values
/    quotient               integers, floats, complex values
%    remainder              integers

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

<<   left shift             integer << unsigned integer
>>   right shift            integer >> unsigned integer

Integer operators

对于两个整数值xy ,整数商q = x / y且余数r = x % y满足以下关系:

x = q*y + r  and  |r| < |y|

x / y截断为零( "截断除法" ).

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

一个例外是,如果被除数x为的int型的最负值x ,商q = x / -1等于x (和r = 0 ),由于二进制补码整数溢出

			 x, q
int8                     -128
int16                  -32768
int32             -2147483648
int64    -9223372036854775808

如果除数为常数 ,则不得为零. 如果除数在运行时为零,则会发生运行时恐慌 . 如果被除数是非负数,并且除数是2的恒定乘方,则除法可以用向右移位代替,计算余数可以用按位与运算代替:

 x     x / 4     x % 4     x >> 2     x & 3
 11      2         3         2          3
-11     -2        -3        -3          1

移位运算符将左操作数移位右操作数指定的移位计数,该移位计数必须为正. 如果运行时班次计数为负,则会发生运行时恐慌 . 如果左操作数是有符号整数,则移位运算符将执行算术移位;如果是无符号整数,则将进行逻辑移位. 班次计数没有上限. 移位的行为就好像左操作数对n移位了1次n次. 结果, x << 1x*2相同, x >> 1x/2相同,但被截断为负无穷大.

For integer operands, the unary operators +, -, and ^ are defined as follows:

+x                          is 0 + x
-x    negation              is 0 - x
^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
                                      and  m = -1 for signed x

Integer overflow

, where is the bit width of the unsigned integer 's type. 对于无符号整数值,运算+-*<<以2 为模,其中是无符号整数类型的位宽. 松散地说,这些无符号整数运算在溢出时会丢弃高位,并且程序可能依赖于"环绕".

对于带符号的整数, +-*/<<可能会合法溢出,并且结果值存在,并由带符号的整数表示形式,操作及其操作数确定性地定义. 溢出不会引起运行时恐慌 . 在不发生溢出的假设下,编译器可能不会优化代码. 例如,可能不会假设x < x + 1始终为真.

Floating-point operators

浮点和复数, +x是相同的x ,而-x是否定x . 超出IEEE-754标准时,未指定浮点或复数除以零的结果. 运行时是否发生恐慌是特定于实现的.

一个实现可以将多个浮点操作组合成一个融合操作(可能跨语句),并产生与通过单独执行和舍入指令所获得的值不同的结果. 显式浮点类型转换将舍入到目标类型的精度,从而防止融合会舍弃该舍入.

例如,某些体系结构提供了一种"融合乘法与加法"(FMA)指令,该指令可计算x*y + z而无需舍入中间结果x*y . 这些示例说明Go实现何时可以使用该指令:

// FMA allowed for computing r, because x*y is not explicitly rounded:
r  = x*y + z
r  = z;   r += x*y
t  = x*y; r = t + z
*p = x*y; r = *p + z
r  = x*y + float64(z)

// FMA disallowed for computing r, because it would omit rounding of x*y:
r  = float64(x*y) + z
r  = z; r += float64(x*y)
t  = float64(x*y); r = t + z

String concatenation

可以使用+运算符或+=赋值运算符来连接字符串:

s := "hi" + string(c)
s += " and good bye"

字符串加法通过连接操作数来创建新的字符串.

Comparison operators

比较运算符比较两个操作数,并产生无类型的布尔值.

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal

在任何比较中,第一个操作数必须可分配给第二个操作数的类型,反之亦然.

. 等号运算符==!=适用于可操作数. . 排序运算符<<=>>=适用于已操作数. 这些术语和比较结果定义如下:

如果动态类型相同的两个接口值不具有可比性,则将它们进行比较会导致运行时恐慌 . 此行为不仅适用于直接接口值比较,而且适用于将接口值或结构的数组与接口值字段进行比较.

切片,贴图和函数值不可比较. 但是,作为特殊情况,可以将切片,图或函数值与预先声明的标识符nil进行比较. 还允许将指针,通道和接口值与nil比较,并遵循上面的一般规则.

const c = 3 < 4            // c is the untyped boolean constant true

type MyBool bool
var x, y int
var (
	// The result of a comparison is an untyped boolean.
	// The usual assignment rules apply.
	b3        = x == y // b3 has type bool
	b4 bool   = x == y // b4 has type bool
	b5 MyBool = x == y // b5 has type MyBool
)

Logical operators

逻辑运算符适用于布尔值,并产生与操作数相同类型的结果. 正确的操作数是有条件的.

&&    conditional AND    p && q  is  "if p then q else false"
||    conditional OR     p || q  is  "if p then true else q"
!     NOT                !p      is  "not p"

Address operators

一个操作数x型的T ,写入动作&x生成类型的指针*Tx . , that is, either a variable, pointer indirection, or slice indexing operation; 操作数必须是 ,即变量,指针间接或切片索引操作; 或可寻址结构操作数的字段选择器; 或可寻址数组的数组索引操作. 除可寻址性要求外, x还可为(可能带有括号的) 复合文字 . 如果对x的求值会引起运行时恐慌 ,那么对&x的求值也是如此.

一个操作数x指针类型*T ,指针间接*x表示可变类型的T通过指向x . 如果xnil ,则尝试评估*x将导致运行时恐慌 .

&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)

var x *int = nil
*x   // causes a run-time panic
&*x  // causes a run-time panic

Receive operator

对于通道类型的操作数ch ,接收操作<-ch的值是从通道ch接收的值. 通道方向必须允许接收操作,并且接收操作的类型是通道的元素类型. 表达式将阻塞直到有一个值可用为止. 从nil信道接收将永远阻止. 封闭通道上的接收操作始终可以立即进行,在接收到任何先前发送的值之后,得出元素类型的零值 .

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // wait until clock pulse and discard received value

特殊形式的赋值或初始化中使用的接收表达式

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch

产生另一个无类型的布尔结果,报告通信是否成功. 如果接收到的值是通过成功的发送操作传递到通道的,则ok值为true如果由于通道关闭且为空而生成的零值,则为false .

Conversions

A转换改变类型的表达式的要由所述转换所指定的类型. by the context in which an expression appears. 转换可能从字面上出现在源中,或者可能由表达式出现的上下文 .

转换是形式为T(x)的表达式,其中T是类型,而x是可以转换为类型T的表达式.

Conversion = Type "(" Expression [ "," ] ")" .

如果类型以运算符*<-开头,或者类型以关键字func开头且没有结果列表,则在必要时必须将其括起来,以避免产生歧义:

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point
<-chan int(c)    // same as <-(chan int(c))
(<-chan int)(c)  // 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类型. 在特殊情况下,可以使用非常数x 相同的规则将整数常数x显式转换为字符串类型 .

转换常量会产生一个类型化的常量.

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

在比较结构类型的标识以进行转换时,将忽略结构标签

type Person struct {
	Name    string
	Address *struct {
		Street string
		City   string
	}
}

var data *struct {
	Name    string `json:"name"`
	Address *struct {
		Street string `json:"street"`
		City   string `json:"city"`
	} `json:"address"`
}

var person = (*Person)(data)  // ignoring tags, the underlying types are identical

Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of x and incur a run-time cost. All other conversions only change the type but not the representation of x.

没有在指针和整数之间转换的语言机制. 软件包unsafe在受限情况下实现了此功能.

Conversions between numeric types

对于非恒定数值的转换,适用以下规则:

  1. 在整数类型之间进行转换时,如果值是有符号整数,则将其符号扩展为隐式无限精度; 否则为零扩展. 然后将其截断以适合结果类型的大小. 例如,如果v := uint16(0x10F0) ,则uint32(int8(v)) == 0xFFFFFFF0 . 转换总是产生一个有效值. 没有溢出迹象.
  2. 将浮点数转换为整数时,将舍弃小数(截断为零).
  3. 将整数或浮点数转换为浮点类型,或将复数转换为另一种复数类型时,结果值将舍入为目标类型指定的精度. 例如,可以使用除IEEE-754 32位数字之外的其他精度来存储float32类型的变量x的值,但是float32(x)表示将x的值四舍五入为32位精度的结果. 同样, x + 0.1可能会使用32位以上的精度,但float32(x + 0.1)不会.

在所有涉及浮点或复杂值的非恒定转换中,如果结果类型不能表示该值,则转换成功,但结果值取决于实现.

Conversions to and from a string type

  1. 将有符号或无符号整数值转换为字符串类型会产生一个包含整数的UTF-8表示形式的字符串. 有效Unicode代码点范围之外的值将转换为"\uFFFD" .
      string('a')//" a"
     string(-1)//" \ ufffd" ==" \ xef \ xbf \ xbd"
     string(0xf8)//" \ u00f8" =="ø" ==" \ xc3 \ xb8"
     输入MyString字符串
     MyString(0x65e5)//" \ u65e5" =="日" ==" \ xe6 \ x97 \ xa5"
    
  2. 将一个字节的切片转换为字符串类型会产生一个字符串,其连续字节是该切片的元素.
      string([] byte {'h','e','l','l','\ xc3','\ xb8'})//"hellø"
     string([] byte {})//""
     string([] byte(nil))//""
    
     输入MyBytes [] byte
     string(MyBytes {'h','e','l','l','\ xc3','\ xb8'})//"hellø"
    
  3. 将一片符文转换为字符串类型会产生一个字符串,该字符串是转换为字符串的各个符文值的串联.
      string([] rune {0x767d,0x9d6c,0x7fd4})//" \ u767d \ u9d6c \ u7fd4" =="白鹏翔"
     string([] rune {})//""
     string([] rune(nil))//""
    
     输入MyRunes [] rune
     string(MyRunes {0x767d,0x9d6c,0x7fd4})//" \ u767d \ u9d6c \ u7fd4" =="白鹏翔"
    
  4. Converting a value of a string type to a slice of bytes type yields a slice whose successive elements are the bytes of the string.
    []byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    []byte("")        // []byte{}
    
    MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    
  5. 将字符串类型的值转换为符文类型的切片会产生一个切片,其中包含字符串的各个Unicode代码点.
      [] rune(MyString("白鹏翔"))// //] rune {0x767d,0x9d6c,0x7fd4}
     [] rune("")// [] rune {}
    
     MyRunes("白鹏翔")// [] rune {0x767d,0x9d6c,0x7fd4}
    

Constant expressions

常量表达式只能包含常量操作数,并在编译时求值.

在合法使用分别为布尔,数字或字符串类型的操作数的任何地方,都可以将未类型化的布尔,数字和字符串常量用作操作数.

常数比较始终会产生无类型的布尔常数. 如果常量移位表达式的左操作数是未类型化的常量,则结果是整数常量;否则,返回整数. 否则,它是与左操作数相同类型的常量,该常量必须是整数类型 .

对无类型常量的任何其他操作都会导致相同类型的无类型常量. 即布尔值,整数,浮点数,复数或字符串常量. 如果二进制运算的非类型化操作数(除移位以外)是不同类型的,则结果是该操作数的类型,该类型出现在此列表的后面:整数,符文,浮点数,复数. 例如,将一个未类型化的整数常量除以一个未类型化的复数常量会得出一个未类型化的复数常量.

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)

将内置函数complex应用于无类型的整数,符文或浮点常量会产生无类型的复数常量.

const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
const iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)

常量表达式总是精确地求值; 中间值和常量本身可能需要比该语言中任何预声明类型支持的精度大得多的精度. 以下是法律声明:

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

常数除法或余数运算的除数不得为零:

3.14 / 0.0   // illegal: division by zero

constants must always be accurately representable by values of the constant type. 常量的值必须始终可以由常量类型的值准确表示 . 以下常量表达式是非法的:

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)

一元按位补数运算符^所使用的掩码与非常数规则匹配:对于无符号常量,掩码全为1,对于有符号和无类型常量,掩码全为-1.

^1         // untyped integer constant, equal to -2
uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // same as int8(-2)
^int8(1)   // same as -1 ^ int8(1) = -2

实现限制:编译器在计算无类型浮点数或复杂常量表达式时可能会使用舍入; 请参阅常量部分中的实现限制. 这种舍入可能会导致浮点常量表达式在整数上下文中无效,即使使用无限精度计算时它是整数,反之亦然.

Order of evaluation

在程序包级别, 初始化依赖关系确定变量声明中各个初始化表达式的求值顺序. 否则,在评估表达式,赋值或return语句操作数时,所有函数调用,方法调用和通信操作都将按从左到右的词汇顺序进行评估.

例如,在(本地函数)分配中

y[f()], ok = g(h(), i()+x[j()], <-c), k()

函数调用和通信按照f()h()i()j()<-cg()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

At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:

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()的顺序进行.

根据运算符的关联性评估单个表达式内的浮点运算. 显式括号会通过覆盖默认的关联性来影响评估. 在表达式x + (y + z) ,加法y + z在加x之前执行.

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

prevents execution of all statements that lexically appear after it in the same block . 阻止执行在同一块中按词法出现的所有语句. 以下语句终止:

  1. A "return" or "goto" statement.
  2. 调用内置函数panic .
    • 语句列表以终止语句结尾的 .
      • " if"语句 ,其中:
        • 存在" else"分支,并且
        • 两个分支都是终止语句.
      • " for"语句 ,其中:
        • 没有引用" for"语句的" break"语句,并且
        • 循环条件不存在.
      • 一个" switch"语句 ,其中:
        • 没有引用" switch"语句的" break"语句,
        • 有默认情况,并且
        • 该语句在每种情况下(包括默认值)都以终止语句或可能标记为" fallthrough"的语句结尾.
      • "选择"语句 ,其中:
        • 没有引用" select"语句的" break"语句,并且
        • 每种情况下的语句列表(包括缺省值,如果有的话)以终止语句结尾.
      • 标签的语句标记终止语句.

      所有其他语句不终止.

      如果列表不为空并且其最终非空语句正在终止,则该语句列表以终止语句结尾.

      Empty statements

      空语句不执行任何操作.

      EmptyStmt = .
      

      Labeled statements

      带标签的语句可能是一个目标gotobreakcontinue发言.

      LabeledStmt = Label ":" Statement .
      Label       = identifier .
      
      Error: log.Panic("error encountered")
      

      Expression statements

      除特定的内置函数外,函数和方法调用以及接收操作可以出现在语句上下文中. 此类声明可以用括号括起来.

      ExpressionStmt = Expression .
      

      The following built-in functions are not permitted in statement context:

      append cap complex imag len make new real
      unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
      
      h(x+y)
      f.Close()
      <-ch
      (<-ch)
      len("foo")  // illegal if len is the built-in function
      

      Send statements

      A send statement sends a value on a channel. The channel expression must be of channel type, the channel direction must permit send operations, and the type of the value to be sent must be assignable to the channel's element type.

      SendStmt = Channel "<-" Expression .
      Channel  = Expression .
      

      在开始通信之前,将对通道和值表达式进行评估. 通信将阻塞,直到发送可以继续. 如果接收器准备就绪,则可以在无缓冲的通道上进行发送. 如果缓冲区中有空间,则可以在缓冲通道上进行发送. 在封闭通道上进行发送会引起运行时恐慌 . nil信道上的发送将永远阻塞.

      ch <- 3  // send value 3 to channel ch
      

      IncDec statements

      " ++"和"-"语句将它们的操作数增加或减少无类型常量 1 . 与赋值一样,操作数必须是可寻址的或映射索引表达式.

      IncDecStmt = Expression ( "++" | "--" ) .
      

      以下赋值语句在语义上是等效的:

      IncDec statement    Assignment
      x++                 x += 1
      x--                 x -= 1
      

      Assignments

      Assignment = ExpressionList assign_op ExpressionList .
      
      assign_op = [ add_op | mul_op ] "=" .
      

      各左侧操作数必须是可寻址的 ,地图索引表达,或(对于=分配仅) 空白标识符 . 操作数可以用括号括起来.

      x = 1
      *p = f()
      a[i] = 23
      (k) = <-ch  // same as: k = <-ch
      

      x = y where is a binary arithmetic operator is equivalent to x = x (y) but evaluates x only once. x = y其中是二进制算术运算符 )与x = x (y)等效,但仅对x求值一次. = construct is a single token. =构造是单个标记. 在赋值操作中,左手表达式列表和右手表达式列表都必须恰好包含一个单值表达式,并且左手表达式不能为空白标识符.

      a[i] <<= 2
      i &^= 1<<n
      

      元组分配将多值操作的各个元素分配给变量列表. 有两种形式. 首先,右侧操作数是单个多值表达式,例如函数调用, 通道映射操作或类型断言 . 左侧的操作数数量必须与值的数量匹配. 例如,如果f是一个返回两个值的函数,

      x, y = f()
      

      将第一个值赋给x ,第二个值赋给y . th expression on the right is assigned to the th operand on the left: 在第二种形式中,左边的操作数的数量必须等于右边的表达式的数量,每个表达式都必须是单值,并且右边的第个表达式被分配给左边的第个操作数:

      one, two, three = '一', '二', '三'
      

      空白标识符提供了一种忽略分配中右侧值的方法:

      _ = x       // evaluate x but ignore it
      x, _ = f()  // evaluate f() but ignore second result value
      

      作业分两个阶段进行. 首先,左侧的索引表达式指针间接操作数(包括选择器中的隐式指针间接寻址 )和右侧的表达式的操作数均按通常的顺序求值 . 其次,分配是从左到右执行的.

      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
      
      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}
      

      在分配中,每个值都必须可分配给为其分配了操作数的类型,并具有以下特殊情况:

      1. 任何类型的值都可以分配给空白标识符.
      2. 如果将未类型化的常量分配给接口类型或空白标识符的变量,则该常量首先隐式转换为其默认类型 .
      3. 如果将未类型化的布尔值分配给接口类型或空白标识符的变量,则首先将其隐式转换为bool类型.

      If statements

      " If"语句根据布尔表达式的值指定两个分支的条件执行. 如果表达式的计算结果为true,则执行" if"分支,否则,如果存在,则执行" else"分支.

      IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
      
      if x > max {
      	x = max
      }
      

      该表达式之前可以有一个简单的语句,该语句在对表达式求值之前执行.

      if x := f(); x < y {
      	return x
      } else if x > z {
      	return z
      } else {
      	return y
      }
      

      Switch statements

      " Switch"语句提供多路执行. 将表达式或类型说明符与" switch"内的" case"进行比较,以确定要执行的分支.

      SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
      

      有两种形式:表达式开关和类型开关. 在表达式开关中,个案包含与开关表达式的值进行比较的表达式. 在类型开关中,案例包含与专门注释的开关表达式的类型进行比较的类型. switch表达式在switch语句中仅计算一次.

      Expression switches

      在表达式开关中,对开关表达式进行求值,而不需要为常量的case表达式则从左至右和从上至下进行求值; 第一个等于switch表达式的触发器触发相关案例的语句的执行; 其他情况将被跳过. 如果没有大小写匹配并且存在"默认"大小写,则执行其语句. 默认情况下最多可以有一种情况,它可能出现在" switch"语句中的任何位置. 缺少的switch表达式等效于布尔值true .

      ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
      ExprCaseClause = ExprSwitchCase ":" StatementList .
      ExprSwitchCase = "case" ExpressionList | "default" .
      

      如果switch表达式的计算结果为无类型常量,则首先将其隐式转换为其默认类型 ; 如果它是无类型的布尔值,则首先将其隐式转换为bool类型. 预先声明的无类型值nil不能用作开关表达式.

      如果case表达式是无类型的,则首先将其隐式转换为switch表达式的类型. 对于每个(可能转换的)情况表达式x和切换表达式的值tx == t必须是有效的比较 .

      换句话说,将switch表达式视为用来声明和初始化没有显式类型的临时变量t ; 它是针对每个案例表达式x进行相等性测试的t值.

      在case或default子句中,最后一个非空语句可以是一个(可能标记为" fallthrough"语句,以指示控制权应从该子句的末尾流到下一个子句的第一个语句. 否则,控制流到" switch"语句的末尾. 除表达式开关的最后一个子句外," fallthrough"语句可能作为所有其他语句的最后一个语句出现.

      开关表达式之前可以有一个简单的语句,该语句在计算表达式之前执行.

      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()
      }
      

      实现限制:编译器可能不允许多个case表达式求值相同的常量. 例如,当前的编译器在case表达式中不允许重复的整数,浮点数或字符串常量.

      Type switches

      类型开关比较类型而不是值. 否则它类似于表达式开关. 它由一个特殊的开关表达式标记,该表达式具有使用保留字type而不是实际类型的类型断言的形式:

      switch x.(type) {
      // cases
      }
      

      然后,案例将实际类型T与表达式x的动态类型进行匹配. 与类型声明一样, x必须是接口类型 ,并且在案例中列出的每个非接口类型T必须实现x的类型. 在类型开关的情况下列出的类型必须全部不同 .

      TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
      TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
      TypeCaseClause  = TypeSwitchCase ":" StatementList .
      TypeSwitchCase  = "case" TypeList | "default" .
      TypeList        = Type { "," Type } .
      

      TypeSwitchGuard可以包含一个简短的变量声明 . 使用该格式时,该变量在每个子句的隐式块的TypeSwitchCase的末尾声明. 在大小写恰好列出一种类型的子句中,变量具有该类型. 否则,变量具有TypeSwitchGuard中表达式的类型.

      案例可以使用预先声明的标识符nil代替类型; 当TypeSwitchGuard中的表达式为nil接口值时,将选择这种情况. 最多可能nil案例.

      给定类型为interface{}的表达式x ,以下类型开关:

      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{})
      }
      

      could be rewritten:

      v := x  // x is evaluated exactly once
      if v == nil {
      	i := v                                 // type of i is type of x (interface{})
      	printString("x is nil")
      } else if i, isInt := v.(int); isInt {
      	printInt(i)                            // type of i is int
      } else if i, isFloat64 := v.(float64); isFloat64 {
      	printFloat64(i)                        // type of i is float64
      } else if i, isFunc := v.(func(int) float64); isFunc {
      	printFunction(i)                       // type of i is func(int) float64
      } else {
      	_, isBool := v.(bool)
      	_, isString := v.(string)
      	if isBool || isString {
      		i := v                         // type of i is type of x (interface{})
      		printString("type is bool or string")
      	} else {
      		i := v                         // type of i is type of x (interface{})
      		printString("don't know the type")
      	}
      }
      

      类型切换防护的前面可以有一个简单的语句,该语句在评估防护之前执行.

      The "fallthrough" statement is not permitted in a type switch.

      For statements

      " for"语句指定重复执行一个块. 有三种形式:迭代可以由单个条件," for"子句或" range"子句控制.

      ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
      Condition = Expression .
      

      For statements with single condition

      最简单的形式是," for"语句指定一个块的重复执行,只要布尔条件的值为真即可. 在每次迭代之前评估条件. 如果不存在该条件,则它等于布尔值true .

      for a < b {
      	a *= 2
      }
      

      For statements with for clause

      and a statement, such as an assignment, an increment or decrement statement. 带有ForClause的" for"语句也受其条件控制,但是另外,它可以指定和语句,例如赋值,递增或递减语句. init语句可以是简短的变量声明 ,但post语句不能. 由init语句声明的变量在每次迭代中都会重复使用.

      ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
      InitStmt = SimpleStmt .
      PostStmt = SimpleStmt .
      
      for i := 0; i < 10; i++ {
      	f(i)
      }
      

      如果为非空,则在评估第一次迭代的条件之前执行一次init语句; 每次执行该块后(仅在执行该块时)才执行post语句. ForClause的任何元素都可以为空,但是除非只有一个条件,否则分号是必需的. 如果不存在该条件,则它等于布尔值true .

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

      For statements with range clause

      带有" range"子句的" for"语句遍历数组,切片,字符串或映射的所有条目,或通道上接收到的值. to corresponding if present and then executes the block. 对于每个条目,它将分配给相应的如果存在),然后执行该块.

      RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
      

      , which may be an array, pointer to an array, slice, string, map, or channel permitting receive operations . "范围"子句右侧的表达式称为 ,它可以是数组,指向数组的指针,切片,字符串,映射或允许接收操作的通道. 与赋值一样,如果存在赋值,则左侧的操作数必须是可寻址的或映射索引表达式. 它们表示迭代变量. 如果范围表达式是一个通道,则最多允许一个迭代变量,否则最多可以有两个. 如果最后一个迭代变量是空白标识符 ,则range子句等效于没有该标识符的同一子句.

      范围表达式x在开始循环之前被评估一次,但有一个例外:如果最多存在一个迭代变量并且len(x)常量 ,则不评估范围表达式.

      左侧的函数调用每次迭代评估一次. 对于每个迭代,如果存在各自的迭代变量,则会按以下方式生成迭代值:

      Range expression                          1st value          2nd value
      
      array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
      string          s  string type            index    i  int    see below  rune
      map             m  map[K]V                key      k  K      m[k]       V
      channel         c  chan E, <-chan E       element  e  E
      
      1. 对于数组,指向数组的指针或切片值a ,从元素索引0开始以递增顺序生成索引迭代值.如果存在最多一个迭代变量,则范围循环将生成从0到len(a)-1 ,并且不会索引到数组或切片本身. 对于nil切片,迭代次数为0.
      2. 对于字符串值," range"子句在字节索引0开始的字符串中的Unicode代码点上进行迭代.在连续迭代中,索引值将是UTF-8编码的连续代码点中第一个字节的索引.字符串,第二个值为rune类型的值将是相应代码点的值. 如果迭代遇到无效的UTF-8序列,则第二个值将是0xFFFD (Unicode替换字符),并且下一个迭代将在字符串中前进单个字节.
      3. 未指定地图的迭代顺序,并且不能保证每次迭代之间都相同. 如果在迭代过程中删除了尚未到达的映射条目,则不会生成相应的迭代值. 如果在迭代过程中创建了地图条目,则该条目可能在迭代过程中产生或可以被跳过. 对于创建的每个条目以及从一个迭代到下一个迭代,选择可能有所不同. 如果映射为nil ,则迭代次数为0.
      4. 对于通道,所产生的迭代值是在通道上发送的连续值,直到通道关闭为止. 如果channel为nil ,则范围表达式将永远阻塞.

      赋值语句中所述,将迭代值分配给各个迭代变量.

      可以使用简短变量声明:= )的形式由" range"子句声明迭代变量. 在这种情况下,将它们的类型设置为各个迭代值的类型,并且它们的范围是" for"语句的块; 它们在每次迭代中都会重复使用. 如果迭代变量在" for"语句之外声明,则在执行后,它们的值将是上一次迭代的值.

      var testdata *struct {
      	a *[7]int
      }
      for i, _ := range testdata.a {
      	// testdata.a is never evaluated; len(testdata.a) is constant
      	// i ranges from 0 to 6
      	f(i)
      }
      
      var a [10]string
      for i, s := range a {
      	// type of i is int
      	// type of s is string
      	// s == a[i]
      	g(i, s)
      }
      
      var key string
      var val interface {}  // element type of m is assignable to val
      m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
      for key, val = range m {
      	h(key, val)
      }
      // key == last map key encountered in iteration
      // val == map[key]
      
      var ch chan Work = producer()
      for w := range ch {
      	doWork(w)
      }
      
      // empty a channel
      for range ch {}
      

      Go statements

      " go"语句作为一个独立的并发控制线程(或 )在同一地址空间内开始执行函数调用.

      GoStmt = "go" Expression .
      

      表达式必须是函数或方法调用; 不能用括号括起来. 内置函数的调用与表达式语句一样受到限制.

      函数值和参数在调用goroutine中照常进行评估 ,但是与常规调用不同,程序执行不等待调用的函数完成. 相反,该函数开始在新的goroutine中独立执行. 当函数终止时,其goroutine也终止. 如果函数具有任何返回值,则在函数完成时将其丢弃.

      go Server()
      go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
      

      Select statements

      "选择"语句选择一组可能的发送接收操作中的哪一个进行. 它看起来类似于" switch"语句,但所有情况均涉及通信操作.

      SelectStmt = "select" "{" { CommClause } "}" .
      CommClause = CommCase ":" StatementList .
      CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
      RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
      RecvExpr   = Expression .
      

      具有RecvStmt的案例可以将RecvExpr的结果分配给一个或两个变量,可以使用短变量声明来声明 . RecvExpr必须是(可能带有括号)接收操作. 最多可以有一个默认案例,它可以出现在案例列表中的任何位置.

      " select"语句的执行分几个步骤进行:

      1. 对于该语句中的所有情况,输入" select"语句后,接收操作的通道操作数以及send语句的通道表达式和右侧表达式将按源顺序被精确评估一次. 结果是一组要从中接收或发送到的通道,以及要发送的相应值. 无论选择进行哪种通信操作,都会发生该评估中的任何副作用. 带有简短变量声明或赋值的RecvStmt左侧的表达式尚未评估.
      2. 如果可以进行一种或多种通信,则可以通过统一的伪随机选择来选择可以进行的单个通信. 否则,如果存在默认情况,则选择该情况. 如果没有默认情况,则" select"语句将阻塞,直到可以进行至少一种通信为止.
      3. 除非所选情况是默认情况,否则将执行相应的通信操作.
      4. 如果所选案例是带有简短变量声明或赋值的RecvStmt,则将评估左侧表达式并分配接收值(或多个值).
      5. 执行所选案例的语句列表.

      由于nil通道上的通信永远无法进行,因此只有nil通道且没有默认情况的选择将永远被阻止.

      var a []int
      var c, c1, c2, c3, c4 chan int
      var i1, i2 int
      select {
      case i1 = <-c1:
      	print("received ", i1, " from c1\n")
      case c2 <- i2:
      	print("sent ", i2, " to c2\n")
      case i3, ok := (<-c3):  // same as: i3, ok := <-c3
      	if ok {
      		print("received ", i3, " from c3\n")
      	} else {
      		print("c3 is closed\n")
      	}
      case a[f()] = <-c4:
      	// same as:
      	// case t := <-c4
      	//	a[f()] = t
      default:
      	print("no communication\n")
      }
      
      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

      A"返回"语句中的函数F终止的执行F ,以及任选地提供一个或多个结果值. 任何功能推迟通过F之前执行F返回到它的调用者.

      ReturnStmt = "return" [ ExpressionList ] .
      

      在没有结果类型的函数中," return"语句不能指定任何结果值.

      func noResult() {
      	return
      }
      

      有三种方法可以从具有结果类型的函数返回值:

      1. 一个或多个返回值可以在" return"语句中明确列出. 每个表达式必须是单值的,并且可以分配给函数结果类型的相应元素.
          func simpleF()int {
        	 返回2
         }
        
         func complexF1()(re float64,im float64){
        	 返回-7.0,-4.0
         }
        
      2. " return"语句中的表达式列表可以是对多值函数的单个调用. 效果就好像是将从该函数返回的每个值都分配给具有相应值类型的临时变量,然后是列出这些变量的" return"语句,此时适用前一种情况的规则.
          func complexF2()(re float64,im float64){
        	 返回complexF1()
         }
        
      3. 如果函数的结果类型为其结果参数指定名称,则表达式列表可能为空. 结果参数充当普通的局部变量,并且函数可以根据需要为其分配值. " return"语句返回这些变量的值.
          func complexF3()(re float64,im float64){
        	 重新= 7.0
        	 即时通讯= 4.0
        	 返回
         }
        
         func(devnull)Write(p [] byte)(n int,_ error){
        	 n = len(p)
        	 返回
         }
        

      无论如何声明,所有结果值在输入函数时都将初始化为其类型的零值 . 指定结果的" return"语句在执行任何延迟函数之前会设置结果参数.

      实现限制:如果与返回参数同名的另一个实体(常量,类型或变量)在范围之内 ,则编译器可能不允许在"返回"语句中使用空表达式列表.

      func f(n int) (res int, err error) {
      	if _, err := f(n-1); err != nil {
      		return  // invalid return statement: err is shadowed
      	}
      	return
      }
      

      Break statements

      " break"语句终止同一函数内最里面的" for"" switch"" select"语句的执行.

      BreakStmt = "break" [ 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

      " continue"语句在其post语句处开始最里面的" for"循环的下一次迭代. " for"循环必须在同一函数内.

      ContinueStmt = "continue" [ Label ] .
      

      如果有一个标签,则必须是一个封闭的" for"语句的标签,并且该标签将继续执行.

      RowLoop:
      	for y, row := range rows {
      		for x, data := range row {
      			if data == endOfRow {
      				continue RowLoop
      			}
      			row[x] = data + bias(x, y)
      		}
      	}
      

      Goto statements

      " goto"语句将控制权转移到同一函数中带有相应标签的语句.

      GotoStmt = "goto" Label .
      
      goto Error
      

      执行" goto"语句一定不能使任何变量在goto时尚未进入作用域 . 例如,此示例:

      	goto L  // BAD
      	v := 3
      L:
      

      是错误的,因为跳转到标签L跳过v的创建.

      外的" goto"语句不能跳转到该块内的标签. 例如,此示例:

      if n%2 == 1 {
      	goto L1
      }
      for n > 0 {
      	f()
      	n--
      L1:
      	f()
      	n--
      }
      

      是错误的,因为标签L1在" for"语句的块内,但goto不在.

      Fallthrough statements

      " fallthrough"语句将控制权转移到表达式" switch"语句中下一个case子句的第一个语句 . 它只能用作该子句中的最终非空语句.

      FallthroughStmt = "fallthrough" .
      

      Defer statements

      A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

      DeferStmt = "defer" Expression .
      

      表达式必须是函数或方法调用; 不能用括号括起来. 内置函数的调用与表达式语句一样受到限制.

      每次执行" defer"语句时,都会照常评估调用的函数值和参数并重新保存,但不会调用实际函数. 而是,在周围的函数返回之前,立即以延迟的相反顺序调用延迟的函数. any result parameters are set by that return statement but the function returns to its caller. 也就是说,如果周围的函数通过显式的return语句返回 ,则该return语句设置任何结果参数但函数返回其调用者 ,将执行延迟的函数. 如果延迟的函数值评估为nil ,则在调用该函数时(而不是在执行" defer"语句时),执行会发生恐慌 .

      例如,如果延迟函数是函数文字 ,并且周围函数已命名结果参数在文字范围内,则延迟函数可以在返回结果参数之前对其进行访问和修改. 如果延迟函数具有任何返回值,则在函数完成时将其丢弃. (另请参阅" 处理恐慌 "部分.)

      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 42
      func f() (result int) {
      	defer func() {
      		// result is accessed after it was set to 6 by the return statement
      		result *= 7
      	}()
      	return 6
      }
      

      Built-in functions

      内置函数是预先声明的 . 它们像任何其他函数一样被调用,但是其中一些接受类型而不是表达式作为第一个参数.

      内置函数没有标准的Go类型,因此它们只能出现在调用表达式中 ; 它们不能用作函数值.

      Close

      对于通道c ,内置函数close(c)记录该通道将不再发送任何值. 如果c是仅接收通道,则会出错. 发送到或关闭已关闭的通道会导致运行时恐慌 . 关闭nil通道也会引起运行时恐慌 . 在调用close ,并且在接收到任何先前发送的值之后,接收操作将返回通道类型的零值而不会阻塞. 多值接收操作返回接收值以及通道是否关闭的指示.

      Length and capacity

      内置函数lencap接受各种类型的参数,并返回int类型的结果. 该实现确保结果始终适合int .

      Call      Argument type    Result
      
      len(s)    string type      string length in bytes
                [n]T, *[n]T      array length (== n)
                []T              slice length
                map[K]T          map length (number of defined keys)
                chan T           number of elements queued in channel buffer
      
      cap(s)    [n]T, *[n]T      array length (== n)
                []T              slice capacity
                chan T           channel buffer capacity
      

      切片的容量是在基础数组中为其分配了空间的元素数. 任何时候都保持以下关系:

      0 <= len(s) <= cap(s)
      

      nil切片,映射或通道的长度为0. nil切片或通道的容量为0.

      表达len(s)恒定的 ,如果s是字符串常量. 如果s的类型是数组或指向数组的指针并且表达式s不包含通道接收或(非恒定) 函数调用 ,则表达式len(s)cap(s)是常量. 在这种情况下,不会评估s . 否则, lencap调用不是恒定的,并且将评估s .

      const (
      	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
      	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
      	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
      	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
      	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
      )
      var z complex128
      

      Allocation

      内置函数new采用类型T ,在运行时为该类型的变量分配存储,并返回指向它的*T类型的值. 如有关初始值的部分中所述对变量进行初始化.

      new(T)
      

      例如

      type S struct { a int; b float64 }
      new(S)
      

      为类型S的变量分配存储空间,对其进行初始化( a=0b=0.0 ),并返回*S类型的值,其中包含位置的地址.

      Making slices, maps and channels

      内置函数make采用类型T ,该类型必须是切片,映射或通道类型,还可以选择后面是特定于类型的表达式列表. 它返回类型T的值(不是*T ). 存储器按照有关初始值的部分所述进行初始化 .

      Call             Type T     Result
      
      make(T, n)       slice      slice of type T with length n and capacity n
      make(T, n, m)    slice      slice of type T with length n and capacity m
      
      make(T)          map        map of type T
      make(T, n)       map        map of type T with initial space for approximately n elements
      
      make(T)          channel    unbuffered channel of type T
      make(T, n)       channel    buffered channel of type T, buffer size n
      

      每个大小参数nm必须为整数类型或无类型常量 . 大小不变的参数必须是非负数,并且可以由int类型的值表示 ; 如果它是未类型化的常量,则将其指定为int类型. 如果同时提供nm且它们均是常数,则n必须不大于m . 如果n在运行时为负或大于m ,则会发生运行时恐慌 .

      s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
      s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
      s := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int
      s := make([]int, 10, 0)         // illegal: len(s) > cap(s)
      c := make(chan int, 10)         // channel with a buffer size of 10
      m := make(map[string]int, 100)  // map with initial space for approximately 100 elements
      

      使用地图类型和大小提示n调用make将创建一个具有初始空间的地图,以容纳n地图元素. 精确的行为取决于实现.

      Appending to and copying slices

      内置函数在常见的分片操作中appendcopy辅助功能. 对于这两个函数,结果与参数所引用的内存是否重叠无关.

      所述可变参数函数append追加的零个或更多个值xs类型的S ,它必须是一个切片类型,并返回型也所得到的切片, S . 值x传递给类型为...T的参数,其中TS元素类型 ,并且适用各个参数传递规则 . 作为一种特殊情况, append还接受可分配给[]byte类型的第一个参数,并带有字符串类型的第二个参数,后跟... 这种形式附加了字符串的字节.

      append(s S, x ...T) S  // T is the element type of S
      

      如果s的容量不足以容纳其他值,则append分配一个新的,足够大的基础数组,该数组适合现有slice元素和其他值. 否则, append重复使用基础数组.

      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, 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并返回复制的元素数. 这两个参数必须具有相同的元素类型T并且必须可分配给类型[]T的切片. 复制的元素数是len(src)len(dst)的最小值. 在特殊情况下, copy还接受可分配给类型为[]byte且源参数为字符串类型的目标参数. 该表格将字符串中的字节复制到字节片中.

      copy(dst, src []T) int
      copy(dst []byte, src string) int
      

      Examples:

      var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
      var s = make([]int, 6)
      var b = make([]byte, 5)
      n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
      n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
      n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")
      

      Deletion of map elements

      内置函数delete映射 m delete键为k的元素. k的类型必须可分配m的键类型.

      delete(m, k)  // remove element m[k] from map m
      

      如果映射mnil或元素m[k]不存在,则delete为no-op.

      Manipulating complex numbers

      三个函数组合和分解复数. 内置函数complex从浮点的实部和虚部构造一个复杂的值,而realimag提取复杂值的实部和虚部.

      complex(realPart, imaginaryPart floatT) complexT
      real(complexT) floatT
      imag(complexT) floatT
      

      参数的类型和返回值相对应. 对于complex ,这两个参数必须具有相同的浮点类型,并且返回类型是具有相应浮点组成部分的复杂类型: complex64表示float32参数,而complex128表示float64参数. 如果其中一个参数求值为无类型常量,则首先将其隐式转换为另一个参数的类型. 如果两个参数都求值为无类型的常量,则它们必须为非复数或虚部必须为零,并且函数的返回值为无类型的复数常量.

      For real and imag, the argument must be of complex type, and the return type is the corresponding floating-point type: float32 for a complex64 argument, and float64 for a complex128 argument. If the argument evaluates to an untyped constant, it must be a number, and the return value of the function is an untyped floating-point constant.

      real函数和imag函数共同形成complex的逆函数,因此对于z复杂类型Z的值zz == Z(complex(real(z), imag(z))) .

      如果这些函数的操作数均为常数,则返回值为常数.

      var a = complex(2, -2)             // complex128
      const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
      x := float32(math.Cos(math.Pi/2))  // float32
      var c64 = complex(5, -x)           // complex64
      var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
      _ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift
      var rl = real(c64)                 // float32
      var im = imag(a)                   // float64
      const c = imag(b)                  // untyped constant -1.4
      _ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift
      

      Handling panics

      panicrecover两个内置功能可帮助报告和处理运行时紧急情况和程序定义的错误情况.

      func panic(interface{})
      func recover() interface{}
      

      在执行函数F ,显式调用panic运行时panic会终止F的执行. F 推迟的任何功能然后照常执行. 接下来,将运行由F's调用者运行的所有延迟函数,依此类推,直到被执行的goroutine中顶级函数所延迟的任何延迟. 此时,程序终止,并报告错误情况,包括panic的参数值. . 此终止序列称为 .

      panic(42)
      panic("unreachable")
      panic(Error("cannot parse"))
      

      recover功能允许程序管理紧急恐慌例程的行为. 假设函数G延迟了调用recover的函数D ,并且在执行G的同一个goroutine上的函数中发生了恐慌. 当延迟功能运行达到D ,返回值D的号召, recover将被传递到的呼叫价值panic . 如果D正常返回而没有开始新的panic ,恐慌序列将停止. 在这种情况下,将放弃在Gpanic呼叫之间调用的函数状态,并恢复正常执行. 然后, GD之前运行G延迟的任何函数,并且G的执行将通过返回其调用方而终止.

      的返回值recovernil ,如果任何下列条件成立:

      下面示例中的protect函数调用函数参数g并保护调用者免受g引发的运行时恐慌.

      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()
      }
      

      Bootstrapping

      当前的实现提供了一些自举过程中有用的内置函数. 这些功能已记录完整,但不能保证始终保留该语言. 他们不返回结果.

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

      实现限制: printprintln不需要接受任意参数类型,但是必须支持布尔,数字和字符串类型的打印.

      Packages

      . Go程序是通过将链接在一起而构造的. 一个包又由一个或多个源文件构成,它们一起声明了属于该包的常量,类型,变量和函数,并且可以在同一包的所有文件中访问它们. 这些元素可以导出并在另一个包中使用.

      Source file organization

      每个源文件都由一个package子句组成,该子句定义了它所属的package,其后是一组可能为空的导入声明,这些声明声明了要使用其内容的包,然后是一组可能为空的函数,类型,变量,和常数.

      SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
      

      Package clause

      package子句从每个源文件开始,并定义该文件所属的包.

      PackageClause  = "package" PackageName .
      PackageName    = identifier .
      

      PackageName不能为空白标识符 .

      package math
      

      共享相同PackageName的一组文件构成了一个包的实现. 一个实现可能要求包的所有源文件都驻留在同一目录中.

      Import declarations

      An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.

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

      PackageName用于合格标识符中,以访问导入源文件中包的导出标识符. 它在文件块中声明. 如果省略PackageName,则默认为导入包的package子句中指定的标识符. 如果出现一个明显的句点( . )而不是名称,则在该软件包的package块中声明的所有软件包导出标识符都将在导入源文件的file块中声明,并且必须在不使用限定符的情况下进行访问.

      ImportPath的解释取决于实现,但通常是已编译软件包的完整文件名的子字符串,并且可能相对于已安装软件包的存储库而言.

      实现限制:编译器可以仅使用属于Unicode的 L,M,N,P和S常规类别的字符(无空格的图形字符)将ImportPaths限制为非空字符串,并且还可以排除字符!"#$%&'()*,:;<=>?[\]^`{|}和Unicode替换字符U + FFFD.

      假定我们已经编译了一个包含package子句package math的软件包,该软件包导出函数Sin ,并将编译后的软件包安装在由"lib/math"标识的文件中. 下表说明了在各种类型的导入声明之后导入软件包的文件中如何访问Sin .

      Import declaration          Local name of Sin
      
      import   "lib/math"         math.Sin
      import m "lib/math"         m.Sin
      import . "lib/math"         Sin
      

      导入声明声明了导入和导入包之间的依赖关系. 包直接或间接导入自身,或不引用其任何导出标识符直接导入包是非法的. 要仅出于副作用(初始化)导入软件包,请使用空白标识符作为显式软件包名称:

      import _ "lib/math"
      

      An example package

      这是一个完整的Go软件包,可实现并发的主筛.

      package main
      
      import "fmt"
      
      // Send the sequence 2, 3, 4, … to channel 'ch'.
      func generate(ch chan<- int) {
      	for i := 2; ; i++ {
      		ch <- i  // Send 'i' to channel 'ch'.
      	}
      }
      
      // Copy the values from channel 'src' to channel 'dst',
      // removing those divisible by 'prime'.
      func filter(src <-chan int, dst chan<- int, prime int) {
      	for i := range src {  // Loop over values received from 'src'.
      		if i%prime != 0 {
      			dst <- i  // Send 'i' to channel 'dst'.
      		}
      	}
      }
      
      // The prime sieve: Daisy-chain filter processes together.
      func sieve() {
      	ch := make(chan int)  // Create a new channel.
      	go generate(ch)       // Start generate() as a subprocess.
      	for {
      		prime := <-ch
      		fmt.Print(prime, "\n")
      		ch1 := make(chan int)
      		go filter(ch, ch1, prime)
      		ch = ch1
      	}
      }
      
      func main() {
      	sieve()
      }
      

      Program initialization and execution

      The zero value

      当通过声明或对new的调用为变量分配存储空间时,或者通过复合文字或对make的调用创建新值时,并且未提供任何显式初始化,则给出变量或值默认值. for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. 此类变量或值的每个元素的类型均设置 :布尔 false ,数字类型为0 ,字符串为"" ,指针,函数,接口,切片,通道和映射为nil . 此初始化是递归完成的,因此,例如,如果未指定任何值,则结构数组的每个元素的字段都将为零.

      这两个简单的声明是等效的:

      var i int
      var i int = 0
      

      After

      type T struct { i int; f float64; next *T }
      t := new(T)
      

      以下内容成立:

      t.i == 0
      t.f == 0.0
      t.next == nil
      

      之后也是如此

      var t T
      

      Package initialization

      在程序包中,程序包级变量的初始化是逐步进行的,每个步骤都按最早选择变量,而该变量与未初始化的变量无关.

      More precisely, a package-level variable is considered 准备初始化 if it is not yet initialized and either has no initialization expression or its initialization expression has no dependencies on uninitialized variables. Initialization proceeds by repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.

      如果在此过程结束时仍未初始化任何变量,则这些变量是一个或多个初始化周期的一部分,并且该程序无效.

      由右侧的单个(多值)表达式初始化的变量声明的左侧的多个变量将一起初始化:如果初始化左侧的任何变量,则所有这些变量都将初始化在同一步骤中.

      var x = a
      var a, b = f() // a and b are initialized together, before x is initialized
      

      出于程序包初始化的目的, 空白变量与声明中的其他任何变量一样对待.

      在多个文件中声明的变量的声明顺序由向编译器提供文件的顺序确定:在第一个文件中声明的变量先于在第二个文件中声明的任何变量声明,依此类推.

      to them in the source, analyzed transitively. 依赖性分析不依赖于变量的实际值,而仅依赖于源中对它们的词法 ,并进行了传递性分析. 例如,如果变量x的初始化表达式引用的函数的主体引用变量yx取决于y . 特别:

      例如,给定声明

      var (
      	a = c + b  // == 9
      	b = f()    // == 4
      	c = f()    // == 5
      	d = 3      // == 5 after initialization has finished
      )
      
      func f() int {
      	d++
      	return d
      }
      

      初始化顺序为dbca . 请注意,初始化表达式中子表达式的顺序无关紧要:在此示例中, a = c + ba = b + c导致相同的初始化顺序.

      对每个程序包执行依赖性分析; 仅考虑引用当前程序包中声明的变量,函数和(非接口)方法的引用. 如果变量之间存在其他隐藏的数据依赖性,则未指定这些变量之间的初始化顺序.

      例如,给定声明

      var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
      var _ = sideEffect()  // unrelated to x, a, or b
      var a = b
      var b = 42
      
      type I interface      { ab() []int }
      type T struct{}
      func (T) ab() []int   { return []int{a, b} }
      

      变量a将在b之后初始化,但是未指定x是在b之前, ba之间还是在a之后初始化,因此也没有指定sideEffect()被调用的时刻(在x初始化之前或之后).

      Variables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters.

      func init() { … }
      

      甚至可以在单个源文件中为每个包定义多个此类功能. 在package块中, init标识符只能用于声明init函数,而标识符本身未声明 . 因此,不能从程序中的任何地方引用init函数.

      通过将初始值分配给其所有包级变量来初始化不导入的包,然后按它们在源中(可能在多个文件中的顺序)出现的顺序调用所有init函数,以提供给编译器. 如果程序包已导入,则在初始化程序包本身之前先初始化导入的程序包. 如果有多个软件包导入一个软件包,则导入的软件包将仅初始化一次. 通过构造的方式导入程序包可确保没有循环初始化依赖项.

      程序包初始化-变量初始化和init函数的调用-一次在单个goroutine中顺序出现,一个程序包一次. init函数可以启动其他goroutine,这些goroutine可以与初始化代码同时运行. 但是,初始化总是对init函数进行排序:在返回前一个函数之前,它不会调用下一个函数.

      为了确保可重现的初始化行为,鼓励构建系统以词法文件名的顺序向编译器提供属于同一软件包的多个文件.

      Program execution

      with all the packages it imports, transitively. 通过将单个未导入的程序包(称为与该程序包所导入的所有程序包链接起来,可以创建一个完整的程序. 主程序包必须具有程序包名称main并声明一个不带参数且不返回值的main函数.

      func main() { … }
      

      Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

      Errors

      预定义的类型error定义为

      type error interface {
      	Error() string
      }
      

      它是用于表示错误状态的常规接口,其中nil值表示没有错误. 例如,可能定义了从文件读取数据的功能:

      func Read(f *File, b []byte) (n int, err error)
      

      Run-time panics

      equivalent to a call of the built-in function panic with a value of the implementation-defined interface type runtime.Error . 执行错误(例如尝试对数组进行索引超出范围)会触发等效于使用实现定义的接口类型runtime.Error的值调用内置函数panic情况. 该类型满足预先声明的接口类型error . 未指定代表不同的运行时错误条件的确切错误值.

      package runtime
      
      type Error interface {
      	error
      	// and perhaps other methods
      }
      

      System considerations

      Package unsafe

      编译器已知并可以通过导入路径 "unsafe"访问的内置包unsafe ,为低级编程提供了便利,包括违反类型系统的操作. 为了安全起见,使用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值. 可以将基础类型 uintptr任何指针或值都转换为基础类型Pointer的类型,反之亦然. 在Pointeruintptr之间转换的效果是实现定义的.

      var f float64
      bits = *(*uint64)(unsafe.Pointer(&f))
      
      type ptr unsafe.Pointer
      bits = *(*uint64)(ptr(&f))
      
      var p ptr = nil
      

      函数AlignofSizeof接受任何类型的表达式x并分别返回假设变量v的对齐方式或大小,就好像v是通过var v = x声明的一样.

      函数Offsetof一个(可能带有括号的) 选择器 sf ,表示由s*s表示的结构的字段f ,并以字节为单位返回相对于该结构的地址的字段偏移量. 如果f是一个嵌入式字段 ,则它必须是可访问的,而没有指针间接通过该结构的字段. 对于具有字段f的结构s

      uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
      

      ; 计算机体系结构可能需要内存地址; . 也就是说,如果变量的地址是一个因子的倍数,则该变量的类型为 . 函数Alignof接受一个表示任何类型变量的表达式,并以字节为单位返回变量(类型)的对齐方式. 对于变量x

      uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
      

      调用AlignofOffsetofSizeofuintptr类型的编译时常量表达式.

      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
      

      保证以下最小对齐属性:

      1. 对于任何类型的变量xunsafe.Alignof(x)至少为1.
      2. 用于可变x结构类型: unsafe.Alignof(x)是最大的所有值的unsafe.Alignof(xf)的每个字段fx ,但至少1.
      3. 对于数组类型的变量xunsafe.Alignof(x)与数组元素类型的变量的对齐方式相同.

      如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零. 两个不同的零大小变量在内存中可能具有相同的地址.

      by  ICOPY.SITE