Introduction#
This is a reference manual for the Go language. You can also visit golang.org for more information and other documentation.
Go is a general-purpose programming language designed with system programming in mind. It is strongly typed, has a garbage collection mechanism, and natively supports concurrent programming. Go programs consist of one or more packages, which allows for efficient management of dependencies.
The syntax of Go is concise and regular, making it easy for automation tools to analyze the code, such as integrated development environments.
Grammar#
The syntax is defined using an extended Backus-Naur form.
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 are expressions composed of lexical units and the following operators (in increasing order of precedence):
| or
() grouping
[] optional (0 or 1 occurrence)
{} repetition (0 to n occurrences)
Lowercase production names are used to distinguish them from lexical units. Non-terminals use camel case. Lexical units are composed of double quotes or backticks.
a...b
represents any character between a
and b
. The ellipsis ...
can also denote the omission of more detailed enumerations and code snippets in the specification. The character ...
is not a lexical unit in Go.
Source Code Representation#
Go source code uses UTF-8 encoded Unicode text. However, it is not fully normalized; single-accented code points are different from code points composed of the same character and accent; the former is considered two code points. In simple terms, the document will use non-normalized term characters in the source code text to represent a Unicode code point.
Each code point is distinct; the uppercase and lowercase forms of the same character represent different characters.
Implementation restriction: To be compatible with other tools, the compiler does not allow NUL characters (U+0000) in UTF-8 encoded source text.
Implementation restriction: To be compatible with other tools, if the source text starts with a UTF-8 byte order mark (U+FEFF), the compiler will ignore it. Byte order marks should not appear anywhere in the source text.
Characters#
These words represent categories of Unicode characters:
newline = /* Unicode code point U+000A */ .
unicode_char = /* any Unicode code point excluding newline */ .
unicode_letter = /* a letter ("Letter") type Unicode code point */ .
unicode_digit = /* a digit ("Number, decimal digit") type Unicode code point */ .
In the Unicode 8.0 standard, section 4.5 "General Categories" defines character categories. Go can handle any character set, including Lu, Li, Lt, Lm, or Lo as Unicode letters, and can treat the numeric character set Nd as Unicode digits.
Letters and Digits#
We consider the underscore _
(U+005F) to be a letter:
letter = unicode_letter | "_" .
decimal_digit = "0" … "9" .
octal_digit = "0" … "7" .
hex_digit = "0" … "9" | "A" … "F" | "a" … "f" .
Lexical Elements#
Comments#
Comments are documentation for the program. There are two forms in Go:
- Single-line comments start with
//
and end at the end of the line. - General comments start with
/*
and end with*/
.
Comments cannot be nested within other comments, string literals, or rune literals. General comments without newlines are connected by whitespace; otherwise, each comment segment starts on a new line.
Lexical Elements#
Lexical elements constitute the vocabulary of the Go language. There are four types: identifiers, keywords, operators/punctuation, and literals. Whitespace can be spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), or line feeds (U+000A). It is ignored and generally used to separate different lexical elements. Line feeds or end-of-file (EOF) may also trigger the compiler to append semicolons at the end of lines or at the end of files. During the breakdown of lexical elements in the source code, the longest character sequence that can form a valid lexical element is taken as the next lexical element.
Semicolons#
Regular syntax uses semicolons ";" as terminators in many productions. The Go program omits most semicolons following these two rules:
-
A semicolon is automatically inserted at the end of a line if the last lexical element of that line is one of the following:
- An identifier.
- An integer, floating-point number, imaginary number, rune, or string literal.
- One of the keywords
break
,continue
,fallthrough
, orreturn
. - One of the operators/punctuation
++
,--
,)
,]
, or}
.
-
To support complex statements that occupy a single line, semicolons adjacent to
)
or}
are omitted.
To reflect idiomatic usage, all examples in this document omit semicolons based on the above rules.
Identifiers#
Identifiers represent units of program entities, such as variables and types. An identifier consists of one or more letters and digits. The first character of an identifier must be a letter.
identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ
Go has predefined some identifiers.
Keywords#
The following keywords are reserved and cannot be used as identifiers:
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#
The following character sequences are used to represent operators (including assignment operators) and punctuation:
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^=
Integer Literals#
Integer literals are a sequence of digits equivalent to integer constants. A prefix can be used to specify a non-decimal base: 0 indicates octal, 0x/0X indicates hexadecimal. In hexadecimal literals, letters a-f and A-F represent the numbers 10-15.
int_lit = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( "1" … "9" ) { decimal_digit } .
octal_lit = "0" { octal_digit } .
hex_lit = "0" ( "x" | "X" ) hex_digit { hex_digit } .
42
0600
0xBadFace
170141183460469231731687303715884105727
Floating-point Literals#
Floating-point literals are a decimal number equivalent to floating-point constants. They consist of an integer part, a decimal point, a fractional part, and an exponent part. The integer part and fractional part are linked by a decimal point; the exponent part consists of the character e
/ E
followed by a signed exponent. Either the integer part or the fractional part can be omitted; either the decimal point or the exponent part can be omitted.
float_lit = decimals "." [ decimals ] [ exponent ] |
decimals exponent |
"." decimals [ exponent ] .
decimals = decimal_digit { decimal_digit } .
exponent = ( "e" | "E" ) [ "+" | "-" ] decimals .
0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
Imaginary Literals#
Imaginary literals are a decimal number equivalent to the imaginary part of complex constants. They consist of a floating-point number or integer followed by the lowercase letter i.
imaginary_lit = (decimals | float_lit) "i" .
0i
011i // == 11i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
Rune Literals#
Rune type literals are equivalent to rune constants. They are integers representing Unicode code points. Rune type literals are represented as one or more characters enclosed in single quotes, like 'x' or '\n'. In single quotes, all characters can be displayed directly except for newline and unescaped single quotes. The value of characters enclosed in single quotes is equal to the value of the character in Unicode encoding, while multi-character sequences starting with a backslash will translate the value into multiple formats.
Using quotes to represent a single character is the simplest way; since Go's source text is UTF-8 encoded, an integer may represent multiple UTF-8 bytes. For example, 'a' can be represented using a single byte for character a, Unicode encoding U+0061, value 0x61, while 'ä' is represented using two bytes for the accented a, Unicode encoding U+00E4, value 0xe4.
Backslashes can encode any value into ASCII text. There are four ways to represent integer values as numeric constants: \x
followed by two hexadecimal digits; \u
followed by four hexadecimal digits; \U
followed by eight hexadecimal digits; and \
followed by three octal digits. In each case, the corresponding base is used to represent the literal integer value.
Although these four methods represent integers, their valid ranges are not the same. Octal can only represent integers within 0 - 255. Hexadecimal can fully meet the requirements. \u
and \U
can represent Unicode code points, but some of those values are invalid, especially values above 0x10FFFF.
Backslashes combined with the following characters have special meanings:
\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 (only valid in rune literals)
\" U+0022 double quote (only valid in string literals)
All other sequences starting with a backslash are illegal in rune rules.
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
'aa' // invalid: too many characters
'\xa' // invalid: missing hexadecimal digits
'\0' // invalid: missing octal digits
'\uDFFF' // invalid: surrogate half
'\U00110000' // invalid: illegal Unicode code point
String Literals#
String literals represent string constants obtained from sequences of characters. They have two formats: raw string literals and interpreted string literals.
Raw strings are enclosed in backticks (`foo`
). Characters other than backticks are displayed. Raw strings consist of characters between backticks (default UTF-8 encoding). Its value is all characters within the quotes without interpretation (default UTF-8 encoding); in particular, backslashes have no special meaning in strings, and newlines are preserved. The value of a raw string will discard carriage return characters '\r'.
Interpreted strings consist of characters between double quotes ("bar"
). All characters except for newlines and double quotes are displayed. The text between double quotes forms the literal value. The escape rules for backslashes are basically the same as for rune literals (the difference is that \'
is illegal, while \"
is legal). Three-digit octal escape sequences (\nnn
) and two-digit hexadecimal escape sequences (\xnn
) represent the corresponding string byte values. Other escape sequences represent the UTF-8 encoding of their respective characters (which may be multi-byte). Therefore, the strings \377
and \xFF
both represent a single byte with a value of 0xFF=255, while ÿ
, \u00FF
, \U000000FF
, and \xc3\xbf
represent the two bytes of the UTF-8 encoded character U+00FF, which are 0xc3 and 0xbf.
string_lit = raw_string_lit | interpreted_string_lit .
raw_string_lit = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc` // equivalent to "abc"
`\n
\n` // equivalent to "\\n\n\\n"
"\n"
"\"" // equivalent to `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800" // invalid: surrogate half
"\U00110000" // invalid: invalid Unicode code point
These examples all represent the same string:
"日本語" // UTF-8 text
`日本語` // UTF-8 text as raw literal
"\u65e5\u672c\u8a9e" // definite Unicode code points
"\U000065e5\U0000672c\U00008a9e" // definite Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // definite UTF-8 bytes
If the source code uses two code points to represent a character, such as an accented letter, putting it in a rune will result in an error (it is not a single code point). In a string, it will display two code points.
Constants#
Constants are divided into: boolean, rune, integer, floating-point, complex, and string types. Among them, rune, integer, floating-point, and complex types are collectively referred to as numeric constants.
The value of a constant can be represented as a rune literal, integer literal, floating-point literal, imaginary literal, string literal, an identifier representing a constant, a constant expression, a type conversion that results in a constant, and some built-in functions that return constants (such as unsafe.Sizeof
, which accepts any value, and cap
or len
, which accept partial expressions, and real
and imag
, which accept complex constants, and complex
, which accepts numeric constants). The boolean type values are predefined constants true
or false
, and the predefined identifier iota
represents an integer constant.
In general, complex constants are a form of constant expression. This will be discussed in detail in the constant expression section.
Numeric constants can represent values of arbitrary precision and will not overflow. Therefore, there are no constants that can represent non-zero, infinity, and non-numeric values.
Constants can specify types or not. Literal constants, true
, false
, iota
, and constants that only contain untyped constant operations are untyped.
Constants can be explicitly specified with specific types through constant declarations and conversions, or they can be implicitly specified with specific types in variable declarations, assignments, or as expression operands. If the value of a constant does not match its type, an error will be raised.
Untyped constants have a default type, which is implicitly converted based on the context in which the constant is used. For example, a short variable declaration i := 0
does not specify the type of i. The default type of an untyped constant can be: bool
, rune
, int
, float64
, complex128
, or string
, with the specific type determined by the value of the constant.
Implementation restriction: Although numeric constants in Go are of arbitrary precision, the compiler internally limits precision during implementation. This means that each compiler implementation must:
-
Ensure that integer constants have at least 256 bits.
-
Ensure that floating-point constants (including complex constants) have at least 256 bits of the significand and at least 16 bits of the signed exponent.
-
Raise an error if the precision of a given integer cannot be represented.
-
Raise an error if floating-point or complex numbers overflow.
-
Round off if floating-point or complex numbers cannot be represented due to precision limitations.
These requirements apply to both literal constants and the results of constant expressions.
Variables#
Variables are locations used to store values. Depending on the variable type, different values can be stored.
Variable declarations, function parameters and return values, declared function signatures, and function literals will reserve storage space for named variables. Calling the built-in new
function or obtaining the address of composite literals will allocate storage space for variables at runtime. These anonymous variables are indirectly referenced through (possibly implicit) pointers.
Variables of types such as arrays, slices, and structs contain many elements or fields internally, and these elements and fields can be accessed directly. The behavior of each element in an array or slice is essentially the same as that of an individual variable.
The static type of a variable can be determined by variable declarations, the type provided to new
, the element types of composite literals, or the types of struct variable declarations. This can be done through new
or type initialization. Variables of interface types also have a definite dynamic type, which is the specific value type assigned to the variable at runtime (exception: the predeclared nil is untyped). The dynamic type may not be the same during the execution of the program, but the value of the interface variable can be assigned to a variable of the same static type.
var x interface{} // x's static type is interface{} value is nil
var v *T // v's static type is *T value is nil
x = 42 // x's dynamic type is int value is 42
x = v // x's dynamic type is *T value is (*T)(nil)
Using a variable in an expression retrieves the value of the variable; this value is the most recently assigned value. If a variable has not been assigned a value, its value is the zero value of that type.
Types#
A type is a collection that includes values and operations & methods for those values. A type can be represented using a type name. Types have various representations: if a type name exists, it can be represented using the type name, or it can also be represented using a type literal composed of existing types.
Type = TypeName | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .
Go has predeclared certain type names and introduced type declarations. Composite types (arrays, structs, pointers, functions, interfaces, slices, maps, channels) can use their type literals.
Each type T has an underlying type. If T is a predefined type or a type literal, then the underlying type is itself. Otherwise, the underlying type of T is the type it references in its type declaration.
type (
A1 = string
A2 = A1
)
type (
B1 string
B2 B1
B3 []B1
B4 B3
)
string
, A1
, A2
, B1
, and B2
have an underlying type of string
. []B1
, B3
, and B4
have an underlying type of []B1
.
Method Set#
A type may have an associated method set. The method set of an interface type can be represented by itself. For other types, the method set of type T consists of all methods with a receiver type of T. The method set of the corresponding pointer type *T consists of all methods with a receiver type of T or *T. If it is a struct type with embedded fields, the method set may also contain more methods; see the struct type section for details. The method set of other types is empty. Each method in the method set has a unique and non-empty method name.
The method set of a type is used to determine the interfaces implemented by the type and the methods that can be called with the type as a receiver.
Boolean Type#
The boolean type represents the collection of predefined constants true
and false
, which represent boolean truth values. The predefined boolean type is bool
; it is created through type declarations.
Numeric Types#
A numeric type represents the collection of all values of integer and floating-point types. The predefined numeric types include:
uint8 8-bit unsigned integer collection (0 to 255)
uint16 16-bit unsigned integer collection (0 to 65535)
uint32 32-bit unsigned integer collection (0 to 4294967295)
uint64 64-bit unsigned integer collection (0 to 18446744073709551615)
int8 8-bit signed integer collection (-128 to 127)
int16 16-bit signed integer collection (-32768 to 32767)
int32 32-bit signed integer collection (-2147483648 to 2147483647)
int64 64-bit signed integer collection (-9223372036854775808 to 9223372036854775807)
float32 IEEE-754 32-bit floating-point number collection
float64 IEEE-754 64-bit floating-point number collection
complex64 Complex number collection with real and imaginary parts as float32
complex128 Complex number collection with real and imaginary parts as float64
byte uint8 alias
rune int32 alias
The value of an n-bit integer has a width of n bits and is represented in two's complement.
The following predefined types have lengths specified by the specific platform:
uint 32 or 64 bits
int same bit count as uint
uintptr unsigned integer that can hold pointer values
To avoid portability issues, all numeric types are defined through type declarations, except for the aliases byte for uint8 and rune for int32. When using different numeric types in expressions, type conversion is required. For example, int32 and int are not the same type, even if they are equal on the specified platform.
String Type#
The string type represents the value type of strings. The value of a string is a sequence of bytes (which may be empty). Once created, the value of a string cannot be modified. The predefined string type is string
, which is defined through type declarations.
The built-in function len
can be used to obtain the length of a string. If the string is a constant, its length is also a constant at compile time. The bytes of a string can be accessed through numeric indices 0 to len(s)-1. Obtaining the address of a string is an illegal operation; if s[i]
is the i-th byte of the string, then &s[i]
is invalid.
Array Type#
An array is a sequence of a fixed number of elements of a single type, where that single type is called the element type. The number of elements indicates the length of the array, which is never negative.
ArrayType = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .
The length is part of the array type; it is a non-negative constant of type int. The built-in function len
can be used to obtain the length of an array. Elements can be accessed by index 0 to len(a)-1
. Arrays are generally one-dimensional, but they can also be multi-dimensional.
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // same as [2]([2]([2]float64))
Slice Type#
A slice describes a contiguous segment of an underlying array and provides access to the elements within that segment. The slice type represents the collection of all slices of an array of element type. An uninitialized slice is represented by nil.
SliceType = "[" "]" ElementType .
Like arrays, slices can be accessed using indices and have lengths, but unlike arrays, their lengths can change at runtime. We can access elements within a slice using indices 0 to len(s)-1
. The index of a slice may be less than the index of the same element in the underlying array.
Once a slice is initialized, it has an underlying array that holds the elements of the slice. Slices and the underlying array may share the same storage space with other slices pointing to that array, while different arrays always have different storage spaces.
The underlying array of a slice may extend beyond the end of the slice; the capacity of the slice is equal to the current length of the slice plus the length of the unused portion of the array; a slice of the same length as its capacity can be sliced from the original slice. The capacity of a slice can be obtained using the built-in function cap(a)
. A new slice of type T can be created using the make
function.
Using the built-in function make
, we can create a new slice of a given element type T. The make
function takes three parameters: slice type, slice length, and slice capacity, where the slice capacity is optional. The slice created by make
allocates a new array that the slice references in the underlying storage.
make([]T, length, capacity)
The purpose of make
is to create a new array and slice it, so the following two notations are equivalent:
make([]int, 50, 100)
new([100]int)[0:50]
Like arrays, slices are generally one-dimensional, but they can also be composed into multi-dimensional slices. All arrays within arrays must be of the same length, but the lengths of slices within slices can vary dynamically, although slices within slices need to be initialized separately.
Struct Type#
A struct is a sequence of named elements, called fields, where each field corresponds to a name and a type. The names of fields can be explicitly specified (list of identifiers) or implicitly specified (embedded fields). In a struct, non-empty fields must be unique.
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
// Empty struct.
struct {}
// Struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
A field that specifies a type but does not specify a name is called an embedded field; embedded fields must specify a type name T or a pointer type *T pointing to a non-interface type, where T cannot be a pointer type. Alternatively, it can be a pointer to a non-interface type. In this case, the type name is treated as the name of the field.
// A struct containing 4 embedded fields 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
}
The following declaration is invalid because field names must be unique.
struct {
T // embedded field *T conflicts with *P.T
*T // embedded field T conflicts with *P.T
*P.T // embedded field T conflicts with *T
}
If x.f
is a valid selector representing the field or method f
, it will call the field or method f
of the embedded field in the struct x
.
Fields derived from embedded fields behave similarly to the original fields of the struct, except that they cannot be used directly in the composite literal of the struct.
Given a struct S and a type T, the following rules generate the combined method set:
- If S contains an embedded field T, then the method set of S and *S includes the method set with receiver type T, while *S includes the method set with receiver type *T.
- If S contains a field *T, then both S and *S include all methods with receiver types T and *T.
When declaring fields, a string tag can be added to that field. This tag will become an attribute of the corresponding field. An empty tag and a default tag are the same. The value of the tag can be obtained through the reflection interface, can be part of the type definition of the struct type, or can be ignored.
struct {
x, y float64 "" // empty tag and default tag are the same
name string "any string is permitted as a tag"
_ [4]byte "ceci n'est pas un champ de structure"
}
// Struct corresponding to a TimeStamp protocol buffer.
// The tag string defines the number corresponding to the protocol buffer field;
// generally, reflect package is used to read them.
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}
Pointer Type#
Pointer types represent the collection of all pointers pointing to variables of a given type. The specified type is called the base type of the pointer. Uninitialized pointer values are nil.
PointerType = "*" BaseType .
BaseType = Type .
*Point
*[4]int
Function Type#
Function types can represent all functions that have the same parameter types and return value types. Uninitialized function type values are nil.
FunctionType = "func" Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
In the parameter and return value lists, the identifier list must either both exist or be omitted. If present, each name represents a parameter/return value of the specified type; these identifiers must be non-empty and cannot be duplicated. If omitted, the specified type's parameters/return values are represented using the corresponding type. Parameter lists and return value lists generally require parentheses, but in the case of a single omitted return value, parentheses can be omitted.
The last parameter of a function can have a prefix ...
. Functions with such parameters are called variadic functions, and they can accept zero or more parameters.
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 Type#
Interface types specify a method set. An interface type variable can hold any type whose method set is a superset of that interface. We can think of a type as implementing an interface. Uninitialized interface type values are nil.
InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
MethodSpec = MethodName Signature | InterfaceTypeName .
MethodName = identifier .
InterfaceTypeName = TypeName .
In the method set of an interface type, each method name must be non-empty and unique.
// A simple File interface
interface {
Read(b Buffer) bool
Write(b Buffer) bool
Close()
}
Multiple types can implement an interface, for example, types S1
and S2
both have the following method set:
func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }
(Here, the type T can represent either S1
or S2
) S1
and S2
both implement the interface File
, regardless of whether the types have other methods.
A type that implements any method set of an interface that is a subset of that interface may implement multiple different interfaces. For example, all types implement the empty interface:
interface{}
Similarly, consider the following definition for the Locker
interface:
type Locker interface {
Lock()
Unlock()
}
If S1
and S2
also implement it:
func (p T) Lock() { … }
func (p T) Unlock() { … }
Then they implement both interfaces Locker
and File
.
An interface T can use another interface E to specify methods. This method is called embedding interface E into interface T. It adds all methods (including exported and unexported methods) from E into interface T.
type ReadWriter interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type File interface {
ReadWriter // equivalent to adding methods from ReadWriter interface
Locker // equivalent to adding methods from Locker interface
Close()
}
type LockedFile interface {
Locker
File // invalid: Lock, Unlock are not unique
Lock() // invalid: Lock is not unique
}
Interface T cannot recursively embed itself or an interface that has already referenced it.
// invalid: Bad cannot embed itself
type Bad interface {
Bad
}
// invalid: Bad1 cannot embed Bad2 which already references it
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
Map Type#
The map type is an unordered collection with unique values as keys.
MapType = "map" "[" KeyType "]" ElementType .
KeyType = Type .
The key type of a map must be comparable using the comparison operators ==
and !=
. Therefore, its key type cannot be a function, map, or slice. If the key is an interface type, the comparison operator must be able to compare its dynamic value. If it cannot, a runtime error will be raised.
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
The number of elements in a map is called its length. For a map m
, its length can be obtained using the built-in function len
, and its length may change at runtime. Elements can be added and retrieved from the map at runtime, and elements can also be removed using the built-in function delete
.
A new and empty map can be initialized using the built-in function make
. It can specify the type of the map and the reserved space:
make(map[string]int)
make(map[string]int, 100)
The reserved space of the map does not fix its length; it can increase its length by adding a certain number of elements (nil maps cannot have elements added). Nil maps and empty maps are equal; only nil maps cannot have elements added.
Channel Type#
Channels provide a means to send and receive values of a specified type between concurrently executing functions. Uninitialized channels are nil.
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
The operator <-
can specify the direction of data flow in the channel. If no direction is specified, the channel is bidirectional by default. Channels can be restricted to read-only and write-only through conversions and assignments.
chan T // can receive and send data of type T
chan<- float64 // can only send float64 type values
<-chan int // can only receive
<-
associates with the leftmost chan
:
chan<- chan int // equivalent to chan<- (chan int)
chan<- <-chan int // equivalent to chan<- (<-chan int)
<-chan <-chan int // equivalent to <-chan (<-chan int)
chan (<-chan int)
Channels can be initialized using the built-in make
function. The make
function can specify the type and capacity of the channel.
make(chan int, 100)
The capacity sets the maximum number of elements that can be buffered. If no capacity is set or the value is 0, the channel is unbuffered, meaning data will only be transmitted when both the sender and receiver are ready. Buffered channels can still successfully send data when the buffer is not full, and can successfully receive data when the buffer is not empty; nil channels cannot transmit data.
The built-in function close
can close a channel. The second return value of the receiving end can be used to inform the receiver whether there is still data in the closed channel.
Channels can be used in send statements and receive operations. The built-in functions len
and cap
can be called directly on channels in multiple goroutines without considering synchronization. The behavior of channels is similar to FIFO queues. For example, one goroutine sends data while another goroutine receives it; the order of received data is the same as the order of sent data.
Properties and Values of Types#
Type Identity#
Two types may be the same or different.
Defined types are always different types. If the underlying types of two types are structurally the same, then they are equal. In general:
-
Two arrays are the same type if their lengths and element types are the same.
-
Two slices are the same type if their element types are the same.
-
Two structs are equal if their fields are in the same order and their field names, field types, and tags are all the same. The field names of unexported fields are always different in different packages.
-
Two pointers are the same type if their base types are the same.
-
Two functions are the same if they have the same parameter and return value lists, and their types are the same; the names of parameters do not have to be the same.
-
Two interfaces are the same if their method sets are identical (the order of methods matters).
-
Two map types are equal if their key types and value types are the same.
-
Two channel types are the same if the object types they contain and the direction of the channel are the same.
Given the following declarations:
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
These types are equal:
A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5
B0, B0, and C0
[]int and []int
struct{ a, b *T5 } and struct{ a, b *T5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
B0 and B1 are not the same type because they are defined separately through type definitions; func(int, float64) *B0
and func(x int, y float64) *[]string
are different because B0 and []string are not the same type.
Assignability#
In the following cases, x can be assigned to a variable of type T (assign x to T):
-
x's type is T.
-
x's type V and T have the same underlying type and at least one of the types T or V is a defined type.
-
T is an interface type and x implements T.
-
x is a channel, and T is a channel type, with types V and T having the same element type, and at least one of the two types is not a defined type.
-
x equals nil and T is a pointer, function, slice, map, channel, or interface type.
-
x is an untyped constant that can represent a value of type T.
Representability#
A value of type T can represent constant x if:
-
The collection of values of type T includes x.
-
T is a floating-point type, and x can be approximated to type T without overflow. The approximation rule uses
IEEE 754 round-to-even
, where negative zero and unsigned zero are the same. Note that the value of a constant will not be negative zero, NaN, or infinite. -
T is a complex type, and the
real(x)
andimag(x)
parts are composed of the floating-point types corresponding to the complex type (float32
orfloat64
).
x T x can represent a value of T because:
'a' byte 97 is in the collection of byte type values
97 rune rune is an alias for int32, 97 is in the collection of 32-bit integer values
"foo" string "foo" is in the collection of string values
1024 int16 1024 is in the collection of 16-bit integer values
42.0 byte 42 is in the collection of 8-bit unsigned integer values
1e10 uint64 10000000000 is in the collection of 64-bit unsigned integer values
2.718281828459045 float32 2.718281828459045 approximates to 2.7182817 in the collection of float32 values
-1e-1000 float64 -1e-1000 approximates to IEEE -0.0, equal to 0
0i int 0 is an integer value
(42 + 0i) float32 42.0 (0 imaginary part) is in the collection of float32 values
x T x cannot represent a value of T because:
0 bool 0 is not in the collection of boolean values
'a' string 'a' is a rune type, it is not in the collection of string type values
1024 byte 1024 is not in the collection of 8-bit unsigned integer values
-1 uint16 -1 is not in the collection of 16-bit unsigned integer values
1.1 int 1.1 is not an integer value
42i float32 (0 + 42i) is not in the collection of float32 values
1e1000 float64 1e1000 overflows when approximated to IEEE
Code Blocks#
Code blocks are declarations and statements enclosed in curly braces.
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .
In addition to explicitly defined code blocks in the source code, there are also some implicit code blocks.
-
The global code block containing all Go code.
-
The package code block containing all code of the package.
-
The file code block containing all code within the file.
-
Each
if
,switch
, andfor
scope forms an implicit block. -
Each condition in
switch
andselect
has its own code block.
Code blocks can be nested and affect scope.
Declarations and Scope#
A declaration can bind identifiers to constants, types, variables, functions, labels, and packages. Every identifier in a program needs to be declared. The same identifier cannot be declared twice in the same code block. The same identifier cannot be declared simultaneously in both file and package code blocks.
The empty identifier can be used in declarations just like other identifiers. However, it does not bind an identifier, effectively acting as if there was no declaration. In the package code block, the init
identifier can only be used as the identifier for the init
function, just like the empty identifier, it does not introduce a new binding.
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
The scope of declared identifiers is the scope in which the identifier is declared.
Go uses blocks to define lexical scope:
-
Predefined identifiers have global scope.
-
All defined top-level identifiers have package scope.
-
The names of imported packages have file scope.
-
Method receivers, function parameters, and return value variables have function scope.
-
The scope of parameters and variable identifiers defined within a function is the scope until the block containing the identifier ends.
Identifiers declared in one code block can be redeclared in any inner code block. In the scope of the inner code block, the identifier refers to the entity declared in the inner code block.
Package statements do not belong to declarations. Package names do not appear in any scope. Their purpose is only to identify multiple files belonging to the same package and to specify the default package name when imported.
Label Scope#
Labels can be declared using label statements and can be used in break
, continue
, and goto
syntax. If a label is declared but not used, it is illegal. The scope of a label is only the function body in which it is defined; it has no scope in earlier recursive function bodies.
Empty Identifier#
The empty identifier is represented by the underscore _
. Unlike regular non-empty identifiers, it has special meanings as an anonymous identifier in declarations, operands, and assignment statements.
Predefined Identifiers#
The following identifiers have been pre-declared in the global scope:
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#
Identifiers can be exported for use by other packages. An identifier is exported if both of the following conditions are met:
- The first letter of the identifier is uppercase (Unicode class
Lu
). - The identifier is declared in package scope or it is a field name/method name.
Any other identifier is unexported.
Uniqueness of Identifiers#
Given a set of identifiers, an identifier is considered unique if it is not the same as any other identifier in the set. Suppose there are two identifiers; if their spellings are different, or if they are in different packages and not exported, then they are different identifiers. Otherwise, in all other cases, the identifiers are considered the same.
Constant Declarations#
Constant declarations bind a series of identifiers to constant expressions. The number of identifiers must equal the number of expressions. The n-th identifier on the left binds to the n-th expression on the right.
ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .
If a type is given, the constant will be of that type, and the value of the expression must be assignable to that type.
If no type is given, the constant will be converted to the type of the corresponding expression. If the value of the expression is an untyped constant, the declared constant is also untyped, and the identifier of the constant represents the value of the constant. For example, even if the fractional part is 0, as long as the expression is a floating-point literal, the constant identifier represents a floating-point constant.
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
In a constant declaration list enclosed in parentheses, all expressions except the first can be omitted. The value and type of an empty expression list are the same as the preceding non-empty expression. The default expression list is equivalent to repeating the previous expression. The number of identifiers must equal the number of expressions. The iota
constant generator is a mechanism for quickly generating sequential values.
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Partyday
numberOfDays // unexported constant
)
Iota#
In constant declarations, the predefined identifier iota
represents consecutive untyped integer constants. Its value is the position of each constant definition in the constant declaration (starting from zero). It can be used to generate a set of associated constants:
const ( // iota is reset to 0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const ( // iota is reset to 0
a = 1 << iota // a == 1
b = 1 << iota // b == 2
c = 3 // c == 3 (not using iota but its value still increments)
d = 1 << iota // d == 8
)
const ( // iota is reset to 0
u = iota * 42 // u == 0 (untyped integer constant)
v float64 = iota * 42 // v == 42.0 (float64 type constant)
w = iota * 42 // w == 84 (untyped integer constant)
)
const x = iota // x == 0 (iota is reset)
const y = iota // y == 0 (iota is reset)
By definition, using iota
multiple times in the same constant definition will yield the same value:
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)
The last example utilizes the implicit repetition of the last non-empty expression list.
Type Declarations#
Type declarations bind an identifier to a type. There are two forms of type declarations: type definitions and alias declarations.
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
Alias Declarations#
Alias declarations bind an identifier name to a specified type.
AliasDecl = identifier "=" Type .
Within the scope of the identifier, it acts as an alias for the type.
type (
nodeList = []*Node // nodeList and []*Node are the same type
Polar = polar // Polar and polar represent the same type
)
Type Definitions#
Type definitions create a new type and bind an identifier to it. The new type has the same underlying type and operations as the given type.
TypeDef = identifier Type .
This type is called a defined type, and it is different from all other types, including the type that created it.
type (
Point struct{ x, y float64 } // Point and struct{ x, y float64 } are different types
polar Point // polar and Point represent different types
)
type TreeNode struct {
left, right *TreeNode
value *Comparable
}
type Block interface {
BlockSize() int
Encrypt(src, dst []byte)
Decrypt(src, dst []byte)
}
Type definitions can define methods with different method sets for boolean, numeric, and string types:
type TimeZone int
const (
EST TimeZone = -(5 + iota)
CST
MST
PST
)
func (tz TimeZone) String() string {
return fmt.Sprintf("GMT%+dh", tz)
}
Variable Declarations#
Variable declarations can create one or more variables and bind corresponding identifiers, specify types, and initial values.
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"
If an expression list is given, the variables will be initialized using the expression according to assignment rules. Otherwise, each variable will be initialized to the zero value of the variable's type.
If a type is specified, the variable will be of that type. If no type is specified, the variable will use the type of the assigned initial value. If the initial value is an untyped constant, it will convert to the default type of the initial value. If it is an untyped boolean value, then the variable's type will be bool
. The value nil
cannot be assigned to a variable that does not specify a type.
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
Implementation restriction: In function bodies, if a variable is declared but not used, the compiler needs to raise an error.
Short Variable Declarations#
The syntax for short variable declarations is:
ShortVarDecl = IdentifierList ":=" ExpressionList .
It is shorter than the normal way of declaring variables with initialization expressions and does not specify types:
"var" IdentifierList = ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w := os.Pipe(fd) // os.Pipe() returns two values
_, y, _ := coord(p) // coord() returns three values; we only care about y
Unlike normal variable declarations, even if a variable has been declared in the same code block, it can be redeclared in a short variable declaration, ensuring that at least one new non-empty variable exists. In summary, variables should only be redeclared during multi-variable short declarations; redeclaring does not use the new variable but assigns a new value to the variable.
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // redeclares offset
a, a := 1, 2 // illegal: declared a twice without a new variable
Short variable declarations can only be used within functions, such as in the context of if
, for
, or switch
statements to declare temporary variables.
Function Declarations#
Function declarations bind identifiers to functions.
FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .
If a function specifies return parameters, the statements in the function body must end with a terminating statement.
func IndexRune(s string, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
// invalid: missing return statement
}
Function declarations can have no function body. Such declarations provide a function declaration and are implemented externally, such as in assembly scripts.
func min(x int, y int) int {
if x < y {
return x
}
return y
}
func flushICache(begin, end uintptr) // implemented externally
Method Declarations#
Methods are functions with receivers, and method declarations bind identifiers to method names and specify the receiver type for the method.
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver = Parameters .
The receiver is specified by adding an extra parameter to the method. This parameter must be a non-variadic parameter. Its type must be T or a pointer type *T (which may include parentheses). T is called the base type of the receiver; it cannot be a pointer or interface type and can only be defined within the same package. After declaration, we consider the method to be bound to the base type and can be accessed through the selector T or *T.
Non-empty receiver identifiers must be unique in the method signature. If the receiver value is not used in the method, the receiver identifier can be omitted. The parameters of functions and methods are treated the same way.
For a base type, the non-empty bound method name must be unique. If the base type is a struct, the non-empty method name cannot duplicate the struct fields.
Given a Point
type, the declaration is:
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
}
Two methods Length
and Scale
are bound to the type *Point
.
The type of a method is a function type with the receiver as the first parameter, for example, the Scale
method:
func(p *Point, factor float64)
However, a function declared in this way is not a method.
Expressions#
Expressions obtain computed values by using operators and functions on operands.
Operands#
Operands represent a simple element in an expression. An operand can be a literal, a non-empty identifier, or a parenthesized expression.
The empty identifier can only appear on the left side of assignment declarations.
Operand = Literal | OperandName | MethodExpr | "(" Expression ")" .
Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent.
Qualified Identifiers#
Qualified identifiers are identifiers prefixed with a package name. Both the package name and the identifier cannot be empty.
QualifiedIdent = PackageName "." identifier .
Qualified identifiers can be used to access identifiers in different packages (which need to be imported). The identifier must be exported and declared in the package-level code block to be accessible.
math.Sin // represents the Sin function in the math package
Composite Literals#
Composite literals can initialize values for structs, arrays, slices, and maps. Each time, it can only create one value. A literal consists of a literal type and an element list enclosed in parentheses. The keys corresponding to the elements can also be declared in front.
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 .
The underlying type of a literal must be a struct, array, slice, or map type (if no type name is specified, this constraint is enforced). The types of the elements and keys must be assignable to the corresponding field and element types; no additional type conversions are allowed. Keys can represent struct field names, indices of slices and arrays, and keys of map types. For map literals, all elements must have keys. If the same field name or constant value corresponds to multiple elements, an error will be raised. If the key of a map type is of a non-constant type, please refer to the evaluation order section.
Struct literals follow these rules:
-
In a struct, keys must be its field names.
-
The order of elements without keys must match the order of field declarations in the struct.
-
If an element specifies a key, then all elements must specify keys.
-
An element list containing keys does not need to specify every field of the struct; default fields will use the zero value of the field type.
-
A literal can omit specifying elements; such a literal equals the zero value of that type.
-
Specifying a non-exported field from a different package will result in an error.
Given the declaration:
type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }
We can use this notation:
origin := Point3D{} // zero value of Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}} // line.q.x is zero value
Array and slice literals follow these rules:
-
Each element is associated with a numeric index marking its position in the array.
-
The keys specified for elements will serve as their indices. Keys must be constants that can represent non-negative
int
type values; if they are constants of specified types, they must be integer constants. -
Elements without specified keys will use the previous index plus one. If the first element does not specify a key, its index is zero.
Taking a slice operation on an array can yield a slice:
tmp := [n]T{x1, x2, … xn}
tmp[0 : n]
In an array, slice, or map type T, elements or keys of the map may have their own literal types; if the literal type and the element or key type are the same, the corresponding type identifier can be omitted. Similarly, if the element or key type is *T
, then their &T
can also be omitted.
[...]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{})}
When composite literals appear between the keywords and parentheses of if
, for
, or switch
statements without being enclosed in parentheses, it will raise a syntax ambiguity. In this special case, the parentheses of the literal will be considered as the code block of the statement. To avoid ambiguity, composite literals must be enclosed in parentheses.
if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }
Here are valid examples of arrays, slices, and maps:
// 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#
Function literals represent anonymous functions.
FunctionLit = "func" Function .
func(a, b int, z float64) bool { return a*b < int(z) }
Function literals can be assigned to variables or called directly.
Function literals are closures. They can reference variables from the enclosing function, and these variables are shared between the enclosing function and the function literal. They will exist until the lifecycle ends.
Primary Expressions#
Primary expressions are operands of unary and binary expressions.
PrimaryExpr =
Operand |
Conversion |
PrimaryExpr Selector |
PrimaryExpr Index |
PrimaryExpr Slice |
PrimaryExpr TypeAssertion |
PrimaryExpr Arguments .
Selector = "." identifier .
Index = "[" Expression "]" .
Slice = "[" [ Expression ] ":" [ Expression ] "]" |
"[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion = "." "(" Type ")" .
Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()
Selectors#
For a primary expression x that is not a package name, the selector expression:
x.f
represents the field or method f of x (sometimes for *x). The identifier f is called the (field/method) selector. It cannot be an empty identifier. The type of the selector expression is the type of f. If x is a package name, refer to qualified identifiers.
The selector f can represent a method or field f of type T. It can also represent an embedded method or field f of type T. The number of nested levels required to access f is called its depth in type T. Fields or methods declared in T have a depth of 0. Fields or methods declared in an embedded field A of T have a depth equal to the depth of f in A plus one.
Selectors follow these principles:
-
For non-pointer/interface types T/*T, the value x represents the first layer of methods/fields. If there is no corresponding f in the first layer, the selector expression is illegal.
-
For interface types I,
x.f
represents the method name f of the dynamic value x. If the method f is not in the method set of interface I, the selector is illegal. -
As an exception, if x is a pointer type and
(*x).f
is a valid selector expression (it can only represent fields, not methods), then(*x).f
can be simplified tox.f
. -
In other cases, x.f is illegal.
-
If x is a pointer type and its value is nil, where f is a struct field, assigning or accessing
x.f
will cause a runtime panic. -
If x is an interface type and its value is nil, calling
x.f
will cause a runtime panic.
For example, given the declaration:
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
The results are:
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
However, the following way is illegal:
q.M0() // (*q).M0 is valid but not a field selector
Method Expressions#
If M is in the method set of type T, then T.M
is a callable function. The function has the same parameter list as M, just with the receiver parameter added at the front.
MethodExpr = ReceiverType "." MethodName .
ReceiverType = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .
Assuming struct T has two methods, one with receiver type T, Mv, and one with receiver type *T, Mp:
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
The expression:
T.Mv
will generate a function equivalent to Mv, just with the first parameter explicitly declared as the receiver. Its signature will be:
func(tv T, a int) int
This function can be called normally, and the following five ways are equivalent:
t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)
Similarly:
(*T).Mp
generates a function signature representing Mp:
func(tp *T, f float32) float32
With a value receiver, we can explicitly obtain a function with a pointer receiver:
(*T).Mv
generates a function signature representing Mv:
func(tv *T, a int) int
Such a function will indirectly create a value as the receiver passed into the underlying method. The method cannot modify the value of the receiver because its address is in the function's call stack.
The last example shows that declaring a function with a value receiver as a method with a pointer receiver is illegal because the method set of pointer receivers does not include the method set of value types.
By using function call syntax, we can obtain the function value from a method. The receiver is the first parameter of the calling function. Given f := T.Mv
, f is called as f(t, 7)
rather than t.f(7)
. To create a function that binds the receiver, we can use a function literal or a method value.
In interface types, defining a function to obtain a function value is legal. The final function call will use the interface type as the receiver.
Index Expressions#
For a primary expression a of type T, the index expression:
a[x]
can represent the value at index x of an array, pointer to an array, slice, string, or map type a. The index x is called the index or key of the map. The following rules apply:
If a is not of map type:
-
The index x must be of integer type or an untyped constant.
-
Constant indices must be non-negative and representable as int type.
-
Untyped constant indices will be treated as int type values.
-
The range of index x must be
0 <= x < len(a)
; otherwise, it is out of bounds.
For array type A:
-
Constant indices must be within valid range.
-
If x is out of bounds at runtime, it will cause a runtime panic.
-
a[x]
represents the element at index x of the array. The type ofa[x]
is the element type of A.
For pointer types of arrays:
a[x]
can represent(*a)[x]
.
For slice types S:
- If x is out of bounds at runtime, it will cause a runtime panic.
a[x]
represents the element at index x of the slice. The type ofa[x]
is the element type of S.
For string types:
-
If string a is a constant, then the constant index must be within valid range.
-
If x is out of bounds at runtime, it will cause a runtime panic.
-
a[x]
represents the byte at index x, which is of type byte. -
Values cannot be assigned to
a[x]
.
For map types M:
-
It must be ensured that the type of x can be assigned to the key type of M.
-
If the map contains a value for key x, then
a[x]
is the value corresponding to key x in the map, and its type is the value type of M. -
If the map value is nil or does not contain that entity, then
a[x]
is the zero value of the element type of M.
Otherwise, a[x]
is illegal.
Based on the map[K]V
type, the index expression of a can use special formats for assignment and initialization syntax.
v, ok = a[x]
v, ok :=