Go编程语言规范
介绍
Go 是一种设计用于系统编程的通用语言。它是强类型和垃圾回收, 并且显式支持并发编程。程序是从包中构建的 , 其属性允许有效地管理依赖关系。现有的实现使用传统的编译 / 链接模型来生成可执行的二进制文件。go 的语法是紧凑和规则的, 允许通过自动工具 (如集成开发环境) 轻松分析。
源文件
源代码是以 UTF-8 编码的 Unicode 文本 。文本不是规范化的, 所以单个重音代码点与组合口音和字母构造的同一个字符不同; 那些被视为两个代码点。为了简单起见, 本文档将使用不合格的术语字符来引用源文本中的 Unicode 代码点。每个代码点是不同的; 例如, 大小写字母是不同的字符。
实现限制: 为了与其他工具兼容, 编译器可能不允许源文本中有 NUL 字符(U + 0000)。
实现限制: 为了与其他工具兼容, 编译器可能会忽略 UTF-8 编码的字节顺序标记(U + FEFF), 如果它是源文本中的第一个 Unicode 代码点。源中的任何其他位置可能禁止字节顺序标记。
字母和数字
下划线字符_(U + 005F)被认为是一个字母。
letter = unicodeletter | ""。
decimal_digit ="0"..."9"。
octal_digit ="0"..."7"。
hex_digit ="0"..."9"| "A"..."F"| "a"..."f"。
注释
注释在程序中有两种形式:
单行注释以字符序列开始, 并在行尾 // 停止。
多行注释以字符序列开始,/* 并以第一个后续字符序列停止 */。
注释不能在符文或 字符串文字或注释内部启动。不包含换行符的一般注释就像一个空格。任何其他评论都像换行。
标识符(变量名)
标识符定义了程序种的主要操作实体, 如变量和类型。标识符由一个或多个字母和数字组成。标识符中的第一个字符必须是一个字母。
identifier = letter {letter |unicode_digit }
a _x9
αβ
一些标识符是预先声明的。
关键词
以下关键字被保留, 不能用作标识符。
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
以下字符序列表示运算符, 分隔符和其他特殊令牌:
- & += &= && == !=( )
- | -= |= || < <= [ ]
- ^ *= ^ = < - > >= { }
...
slice 类型
slice 是底层 array 的连续 Slice 的描述符, 并提供对该阵列中编号序列的元素的访问。slice 类型表示其元素类型的所有 arraySlice 的集合。未初始化的 Slice 的值为 nil。
SliceType ="[""]" ElementType。
像阵列一样, slice 是可索引的并具有长度。slice 的长度 s 可以通过内置函数发现 len; 与 array 不同, 它可能会在执行过程中发生变化。这些元素可以通过整数索引 0 到 len(s)-1。给定元素的 slice 索引可能小于底层 array 中相同元素的索引。
一旦初始化, 一个 slice 总是与一个保存其元素的底层 array 相关联。因此, slice 与其阵列和同一阵列的其他 slice 共享存储; 相比之下, 不同的 array 总是表示不同的存储。
slice 下面的阵列可以延伸超过 slice 的末端。的容量是该程度的量度: 它是 slice 并超出片的阵列的长度的长度的总和; 可以通过从原始 sliceslice 一个新的 Slice 来创建直到该容量 的 Slice。a 可以使用内置函数发现 slice 的容量 cap(a)。
给定元素类型的新的初始化 slice 值 T 使用内置函数进行 make, 该函数 采用 slice 类型和指定长度和可选容量的参数。创建的 slicemake 始终分配返回的 slice 值所参考的新的隐藏 array。也就是执行
make([] T, 长度, 容量)
生成与分配 array 相切的 slice , 因此这两个表达式是等价的:
make([] int,50,100)
new([100] INT)[0:50]
像序列一样, Slice 总是一维的, 但是可以构成更高维度的对象。通过阵列阵列, 内部阵列通过构造总是相同的长度; 然而, 对于 slice(或 slice 阵列), 内部长度可能会动态变化。此外, 内部 slice 必须单独初始化。
结构体
一个结构体是一个命名元素的序列, 称为字段, 每个结构体都有一个名称和一个类型。字段名称可以被明确指定 (IdentifierList) 或隐式(AnonymousField)。在结构体中, 非空白字段名称必须是唯一的。
StructType ="struct""{"{FieldDecl";"}"}"。
FieldDecl =(IdentifierList Type | AnonymousField)[ Tag ]。
AnonymousField = ["*"] TypeName。
Tag = string_lit。
// 一个空结构。
struct {}
// 具有 6 个字段的结构。
struct {
x,y int
转速 float32 //支持中文名
_ float32 // padding
A * [] int
F func()
}
声明为类型但没有显式字段名称的字段是匿名字段, 也称为嵌入字段或类型在 struct 中的嵌入。必须将嵌入式类型指定为类型名称 T 或指向非接口类型名称的指针 * T, 并且 T 本身可能不是指针类型。不合格的类型名称作为字段名称。
// 具有类型 T1,* T2,P.T3 和 * P.T4 的四个匿名字段的结构
struct {
T1 // 字段名称为 T1
* T2 // 字段名称为 T2
P.T3 // 字段名称为 T3
* P.T4 // 字段名称为 T4
x,y int // 字段名称是 x 和 y
}
以下声明是非法的, 因为字段名称在结构类型中必须是唯一的:
struct {
T // 与匿名字段 * T 和 * PT 冲突
* T // 与匿名字段 T 和 * PT 冲突
* PT // 与匿名字段 T 和 * T 冲突
}
如果是表示该字段或方法的合法选择器, 则 在结构体中的匿名字段的 字段或方法 称为 "升级"。
推广字段的行为像一个结构体的普通字段, 除了它们不能用作结构体的复合文字中的字段名称 。
给定一个 struct 类型 S 和一个类型 T, 在 struct 的方法集中包含了提升的方法, 如下所示:
如果 S 包含一个匿名字段 T 的方法设置的 S , 并 S 都包括推进与接收器的方法 T。方法集 S 还包括具有接收者的促销方法 * T。
如果 S 包含一个匿名字段 T, 方法集 S 和 S 两个包括推进与接收器的方法 T 或 *T。
字段声明后面可以有一个可选的字符串文字标记, 它可以成为相应字段声明中所有字段的属性。空标签字符串相当于缺少的标签。标签通过反射界面可见, 并参与结构体的类型标识, 但是会被忽略。
struct {
x,y float64 ""// 空标签字符串就像一个不存在的标签
name string "允许任何字符串作为标签"
_ [4] byte "ceci n'est pas un champ de structure"
}
// 与 TimeStamp 协议缓冲区对应的结构。
// 标签字符串定义协议缓冲区字段号;
// 他们遵循反射包所概述的惯例。
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}
指针类型
指针类型表示给定类型的变量的所有指针集, 称为指针的基本类型。未初始化的指针的值为 nil。
PointerType ="*" BaseType。
BaseType = Type。
*Point
- [4]int
接口类型
一个接口类型指定一个方法集称为其接口。接口类型的变量可以存储任何类型的值, 其方法集是接口的任何超集。据说实现这种类型 的接口。接口类型的未初始化变量的值为 nil。
InterfaceType ="interface""{"{MethodSpec";"}"}"。
MethodSpec = MethodName Signature | InterfaceTypeName。
MethodName = identifier。
InterfaceTypeName = TypeName。
与所有方法集一样, 在接口类型中, 每个方法必须具有 唯一的 非空白名称。
// 一个简单的文件界面
interface {
Read(b Buffer) bool
Write(b Buffer) bool
Close()
}
多个类型可以实现一个接口。例如, 如果两个类型 S1 并且 S2 设置方法
func(p T)Read(b Buffer)bool {return ...}
func(p T)Write(b Buffer)bool {return ...}
func(p T)Close(){...}
其中 T 代表任何一个 S1 或 S2), 则该 File 接口由两者 S1 实现 S2, 并且不管其他方法 S1 以及 S2 可能具有或共享什么。
类型实现包括其方法的任何子集的任何接口, 并且因此可以实现几个不同的接口。例如, 所有类型都实现空接口:
interface{}
类似地, 考虑这个接口规范, 它出现在一个类型声明中 以定义一个接口 Locker:
type Locker interface {
Lock()
Unlock()
}
如果 S1 和 S2 都实现了该接口
func(p T)Lock(){...}
func(p T)解锁(){...}
它们实现了 Locker 接口以及 File 接口。
接口 T 可以使用 (可能是合格的) 接口类型名称 E 代替方法规范。这就是所谓的 嵌入界面 E 中 T; 它将所有 (导出和未导出) 方法添加 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 可能不嵌入自身或循环嵌套自身。
// 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 类型
map 是一种类型的无序组元素, 称为元素类型, 由另一类型的一组唯一键索引, 称为密钥类型。未初始化地图的值为 nil。
MapType ="map""["KeyType"]" ElementType。
KeyType = Type。
的比较运算符 == 和!= 必须为键类型的操作数被完全定义; 因此, 键类型不能是函数, 映射或 slice。如果密钥类型是接口类型, 则必须为动态密钥值定义这些比较运算符; 失败会导致运行时间的恐慌。
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
map 元素的数量称为其长度。对于 map m, 可以使用内置函数获得它 len , 并可能在执行期间更改。可以在执行期间使用分配添加元素, 并使用 索引表达式检索 ; 它们可以用 delete 内置功能删除 。
使用内置函数 make 创建一个新的空 map, 该函数将地图类型和可选的容量提示作为参数:
make(map[string]int)
make(map[string]int, 100)
初始容量不受其大小的限制: map 增长以适应其中存储的项目数量, 但 nil map 外。一个 nil map 相当于不同之处在于可以添加没有元素的空映射。
Channel 管道类型
管道提供了一种机制 同时执行功能 通过通信 发送和 接收 一个指定的元素类型的值。未初始化频道的值为 nil。
ChannelType =("chan"|"chan""< - "|"< - ""chan")ElementType。
可选<- 操作符指定通道方向, 发送或接收。如果没有给定方向, 通道是 双向的。频道可能被限制只发送或仅通过转换或转让接收 。
chan T // 可用于发送和接收 T 类型的值
chan < - float64 // 只能用于发送 float64s
<-chan int // 只能用于接收 int
chan < - chan int // 与 chan 相同< - (chan int)
chan < - <-chan int // 与 chan < - (<-chan int)相同
<-chan <-chan int // 与 <-chan(<-chan int) 相同
chan(<-chan int)
可以使用内置函数创建新的初始化通道值 make, 该函数将通道类型和可选容量作为参数:
make(chan int,100)
元素数量的容量设置通道中缓冲区的大小。如果容量为零或不存在, 则信道是无缓冲的, 并且仅当发送器和接收器都准备就绪时, 通信才能成功。否则, 如果缓冲区未满 (发送) 或不为空(接收), 则通道被缓冲并且通信成功而不阻塞。一个 nil 频道从未准备好进行通信。
通道可以通过内置功能关闭 close。接收运营商的多值分配形式 报告在通道关闭前是否发送了接收到的值。
单个通道可用于 发送语句, 接收操作和对内置函数的调用 cap 以及 len 任何数量的 goroutine 而不进一步同步。频道作为先进先出队列。例如, 如果一个 goroutine 在一个通道上发送值, 另一个 goroutine 接收它们, 则按发送的顺序接收这些值。
类型转换
在以下这些情况下, 值 x 可分配给类型的变量 T ("x 可分配给 T"):
x 的类型是相同的 T。
x 的类型 V, 并 T 具有相同的 底层类型, 至少有一个 V 或 T 不是命名类型。
T 是一种接口类型和 x 实现方式 T。
x 是双向通道值, T 是通道类型 x 的类型 V,T 具有相同的元素类型, 并且至少有一个 V 或 T 不是命名类型。
x 是预先声明的标识符 nil,T 是指针, 功能, slice, 地图, 通道或界面类型。
x 是由类型值表示的无类型常数 T。
代码块
一个块是匹配括号内可能是空的一系列声明和语句。
Block ="{" StatementList "}"。
StatementList = {Statement ";" }。
除了源代码中的显式块之外, 还有一些隐式块:
在全局块包含所有围棋源文本。
每个包都有一个包含该包的所有 Go 源文本的包块。
每个文件都有一个包含该文件中所有 Go 源文本的文件块。
每个 "if", "for" 和 "switch" 语句被认为是在其自己的隐式块中。
"switch" 或 "select" 语句中的每个子句用作隐式块。
代码块嵌套和影响范围。
IOTA
在常量声明中, 预先声明的 iota 标识符表示连续的无类型整数 常量。每当保留字 const 出现在源中并在每个 ConstSpec 之后递增, 它将重置为 0 。它可以用来构造一组相关的常量:
const(// iota 将重置为 0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const(// iota 将重置为 0
a = 1 << iota // a == 1
b = 1 << iota // b == 2
c = 3 // c == 3(iota 不使用但仍然增加)
d = 1 << iota // d == 8
)
const(// iota 将重置为 0
u = iota * 42 // u == 0(无类型整数常量)
v float64 = iota * 42 // v == 42.0(float64 constant)
w = iota * 42 // w == 84(无类型整数常数)
)
const x = iota // x == 0(iota 已被重置)
const y = iota // y == 0(iota 已被重置)
在一个 ExpressionList 中, 每个值的值 iota 相同, 因为它只在每个 ConstSpec 之后递增:
const(
bit0,mask0 = 1 << iota,1 << iota - 1 // bit0 == 1,mask0 == 0
bit1,mask1 // bit1 == 2,mask1 == 1
_,_ // 跳过 iota == 2
bit3,mask3 // 位 3 == 8,mask3 == 7
)
最后一个例子利用最后一个非空表达式列表的隐式重复。
类型声明
类型声明将标识符 (类型名称) 绑定到与现有类型具有相同底层类型的新类型, 并为新类型定义为现有类型定义的操作。新的类型是不同的, 从现有的类型。
TypeDecl ="type"(TypeSpec |"("{TypeSpec ";"}")")。
TypeSpec = identifier 类型。
type IntArray [16]int
type (
Point struct{x, y float64 }
Polar Point
)
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 the base type of PtrMutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex
// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its anonymous field Mutex.
type PrintableMutex struct {
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)
}
变量声明
变量声明创建一个或多个变量, 将对应的标识符绑定到它们, 并给出每个变量的类型和初始值。
VarDecl ="var"(VarSpec |"("{VarSpec ";"}")")。
VarSpec = IdentifierList(Type ["=" 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 是 float64
var i = 42 // 我是 int
var t,ok = x.(T) // t 是 T,ok 是 bool
var n = nil // illegal
实现限制: 如果变量永远不被使用, 编译器可能会在函数体内声明一个变量是非法的。
短变量声明
一个简短的变量声明使用语法:
ShortVarDecl = IdentifierList ":=" ExpressionList。
它是 使用初始化器表达式但不包含类型的常规变量声明的缩写:
"var"IdentifierList = ExpressionList。
i,j:= 0,10
f:= func()int {return 7}
ch:= make(chan int)
r,w:= os.Pipe(fd) // os.Pipe()返回两个值
_,y,_:= coord(p) // coord()返回三个值; 只对 y 坐标感兴趣
与常规变量声明不同, 短变量声明可以重新 声明变量, 前提是它们最初在同一块中被声明 (或参数列表, 如果块是函数体) 具有相同类型, 并且至少一个非空值变量是新的。因此, 重新声明只能出现在多变量的简短声明中。重新声明不会引入新变量; 它只为原始值分配一个新值。
field1,offset:= nextField(str,0)
field2,offset:= nextField(str,offset)// redeclares offset
a,a:= 1,2 // illegal: 如果在其他地方声明了一个或没有新变量的双重声明
短变量声明只能在函数内部出现。在某些上下文中, 例如 "if", "for" 或 "switch" 语句的初始化器, 它们可以用于声明本地临时变量。
函数声明
函数声明将一个标识符 (函数名称) 绑定到一个函数。
FunctionDecl ="func" FunctionName(Function | Signature)。
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)// 外部实现
** 方法声明 **
一种方法是具有接收器的功能。方法声明将标识符, 方法名称绑定到方法, 并将该方法与接收方的基类型相关联。
MethodDecl ="func" Receiver MethodName(Function | Signature)。
Receiver = 参数。
接收方通过方法名称之前的额外参数部分指定。该参数部分必须声明单个非可变参数, 接收器。它的类型必须是形式 T 或 * T(可能使用括号), 其中 T 是一个类型名称。表示的类型 T 称为接收者基类型 ; 它不能是一个指针或接口类型, 它必须在与该方法相同的包中声明。该方法被认为是结合到基底的类型和方法名唯一内是可见的选择器类型 T 或 * T。
非空白接收者标识符在方法签名中必须是 唯一的。如果接收方的值未在方法体内引用, 则其声明中可能会省略其标识符。这一般同样适用于功能和方法的参数。
对于基类型, 与其绑定的方法的非空名称必须是唯一的。如果基类型是一个结构类型, 则非空白方法和字段名必须是不同的。
给定类型 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
}
使用接收器类型 绑定方法 Length 和基类型。 Scale*PointPoint
方法的类型是接收器作为第一个参数的函数的类型。例如, 该方法 Scale 具有类型
func(p * 点, 因子 float64)
但是, 以这种方式声明的函数不是一种方法。
** 表达式 **
表达式通过将运算符和函数应用于操作数来指定值的计算。
** 操作数 **
操作数表示表达式中的基本值。一个操作数可以是一个文字, 一个 (可能是合格的) 非空白标识符, 表示一个 常量, 变量或 函数, 一个产生函数的方法表达式或一个括号化的表达式。
的空白标识符可能显示为仅一个上的左手侧的操作数分配。
operand = 文字 | OperandName | MethodExpr | "(" 表达 ")"。
Literal = BasicLit | CompositeLit | FunctionLit。
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit。
OperandName = identifier | QualifiedIdent。
** 合格标识符 **
合格的标识符是具有包名前缀的标识符。软件包名称和标识符都不能为 空。
QualifiedIdent = PackageName "。" 标识符。
合格标识符访问必须导入的不同包中的标识符。必须在该包的包块中导出并声明该标识符。
math.Sin // 表示包数学中的 Sin 函数
** 复合类型 **
复合类型为结构, array,slice 和 map 构造值, 并在每次评估时创建一个新值。它们由文字的类型组成, 后跟一个括号绑定的元素列表。每个元素可以可选地在相应的键之前。
CompositeLit = LiteralType LiteralValue。
LiteralType = StructType | ArrayType | "[""...""]" ElementType |
SliceType | MapType | TypeName。
LiteralValue ="{"[ ElementList [","]]"}"。
ElementList = KeyedElement {"," KeyedElement }"。
KeyedElement = [ Key ":"] 元素。
Key = FieldName | 表达 | 文字值。
FieldName = 标识符。
Element = Expression | 文字值。
LiteralType 的底层类型必须是一个 struct,array,slice 或 map 类型(语法强制执行此约束, 除非该类型以 TypeName 形式给出)。元素和键的类型必须可分配 给文字类型的相应字段, 元素和键类型; 没有额外的转换。关键字被解释为结构文字的字段名称, array 和片文本的索引以及地图文字的关键字。对于地图文字, 所有元素必须有一个键。指定具有相同字段名称或常量键值的多个元素是一个错误。
对于结构文字, 以下规则适用:
- 键必须是在 struct 类型中声明的字段名称。
- 不包含任何键的元素列表必须按照声明字段的顺序列出每个结构域的元素。
- 如果任何元素有一个键, 每个元素都必须有一个键。
- 包含键的元素列表不需要每个结构域的元素。省略的字段获得该字段的零值。
- 文字可以省略元素列表; 这样一个文字评估它的类型的零值。
- 为属于不同包的结构体的未导出字段指定元素是一个错误。
每个元素都有一个关联的整数索引, 标记其在 array 中的位置。
具有键的元素使用键作为其索引。关键字必须是由值类型表示的非负常数 int; 如果它是类型, 它必须是整数类型。
没有键的元素使用前一个元素的索引加一个。如果第一个元素没有键, 其索引为零。
以复合文字的地址生成一个指向使用文字值初始化的唯一变量的指针。
var pointer * Point3D =&Point3D {y:1000}
array 文字的长度是文字类型中指定的长度。如果在文字中提供的元素少于长度, 则将丢失的元素设置为 array 元素类型的零值。在 array 的索引范围之外提供索引值的元素是一个错误。该符号... 指定等于最大元素索引加一的 array 长度。
buffer:= [10] string {} // len(buffer)== 10
intSet:= [6] int {1,2,3,5} // len(intSet)== 6
days:= [...] string {"Sat","Sun"} // len(days)== 2
slice 文字描述整个底层 array 文字。因此, Slice 文字的长度和容量是最大元素索引加 1。一个片文字有形式
[] T {x1,x2,... xn}
并且是应用于 array 的 slice 操作的缩写:
tmp:= [n] T {x1,x2,... xn}
tmp [0:n]n
在 array,slice 或地图类型 T 的复合文字中, 如果元素或键类型与元素或键类型相同, 则本身是复合文字的元素或地图键可能会删除相应的文字类型 T。类似地, 作为复合文字地址 & T 的元素或键可以在元素或键类型的时候消除 * T。
[...] Point {{1.5,-3.5},{0,0}} // 与 [...] Point {Point {1.5,-3.5},Point {0,0}} 相同}
[] [] int {{1,2,3},{4,5}} // 与 [] [] int {[] int {1,2,3},[] int {4,5}} 相同)
[] [] {{{0,1},{1,2}}} // 与[] [] Point {[] Point {Point {0,1},Point {1,2}}}
map [string] Point {"orig":{0,0}} // 与 map 相同[string] Point {"orig":Point {0,0}}
map [Point] string {{0,0}:"orig"} // 与 map 相同[Point] string {Point {0,0}:"orig"}
当使用 LiteralType 的 TypeName 形式的复合文字显示为关键字和 "if","for" 或 "switch" 语句的块的开头大括号之间的操作数时, 会出现 解析歧义, 而复合文字为不包括在括号, 方括号或大括号中。在这种罕见的情况下, 文字的大括号被错误地解释为引入语句的一个。要解决歧义, 复合文字必须出现在括号中。
if x ==(T {a,b,c} [i]){...}
if(x == T {a,b,c} [i]){...}
如果类型 x 是一个命名的指针类型, 并且 (*x).f 是一个表示一个字段(而不是一个方法) 的有效的选择器表达式, x.f 则为缩写(*x).f。
在所有其他情况下, x.f 是非法的。
如果 x 是指针类型, 并且具有值 nil 并 x.f 表示结构域, 则分配或评估 x.f 将导致运行时紧急。
如果 x 是接口类型并具有该值 nil, 则调用或 评估该方法 x.f 将导致运行时的紧急情况。
** 索引表达式 **
形式的主要表现形式
a[x]
表示 array 的元素, 指向 array,slice,string 或 map 的 a 索引的指针 x。该值 x 分别称为索引或地图键。以下规则适用:
如果 a 不是 map:
- 索引 x 必须是整数类型或无类型; 它是在范围如果 0 <= x < len(a), 否则它是超出范围
一个恒定索引必须是通过 type 的值是非负的和表示的 int
为 a 的 array 类型 A: - 一个恒定索引必须是在范围
- 如果 x 在运行时超出范围, 则会发生运行时紧急
- a[x]是索引处的 array 元素 x, 其类型 a[x]是元素类型 A
对于指向 array 类型 a 的指针: - a[x] 是速记 (*a)[x]
对于 a 的片类型 S: - 如果 x 在运行时超出范围, 则会发生运行时紧急
- a[x]是索引处的 slice 元素 x, 其类型 a[x]是元素类型 S
对于 a 的字符串类型: - 一个恒定索引必须是在范围内, 如果该字符串 a 也是恒定
- 如果 x 在运行时超出范围, 则会发生运行时紧急
- a[x]在索引的非恒定字节值 x 和类型 a[x]就是 byte
- a[x] 可能不会分配给
对于 a 的地图类型 M: - x 的类型必须可以 分配 给键的类型 M
- 如果地图包含带有键的条目 x, a[x]则是带有键的地图值, x 类型 a[x]是值类型 M
- 如果地图是 nil 或不包含这样的条目, a[x]则为 值类型的零值 M
否则 a[x]是非法的。
在特殊表单 的分配或初始化中 使用 a 的类型 的地图上的索引表达式 map[K]V
v,ok = a [x]
v,ok:= a [x]
var v,ok = a [x]
var v,ok T = a [x]
产生一个额外的无类型布尔值。的值 ok 是 true 如果该键 x 存在于地图, 和 false 其它。
分配给 nil 地图的元素会导致 运行时的紧急情况。
**slice 表达式 **
Slice 表达式从字符串, array, 指向 array 或 slice 的指针构造子字符串或 slice。有两个变体: 一个指定一个低和高边界的简单形式, 以及一个完整的表单, 也指定了对容量的约束。
简单的 slice 表达式
对于字符串, array, 指向 array 或 slice 的指针 a, 主表达式
a[low:high]
构造子字符串或 slice。的索引 low 和 high 操作数的选择哪些元素 a 显示在结果中。结果具有从 0 开始的长度等于 high - 的 索引 low。slicearray 后 a
a:= [5] int {1,2,3,4,5}
s:= a [1:4]
slices 有类型[]int, 长度 3, 容量 4 和元素
s [0] == 2
s [1] == 3
s [2] == 4
为了方便, 可以省略任何索引。缺少的 low 索引默认为零; 缺少的 high 索引默认为 slice 操作数的长度:
a[2:] // 与 [2:len(a)] 相同]
a[:3] // 与 [0:3] 相同
a[:] // 与 [0:len(a)] 相同]
如果 a 是指向 array 的指针, a[low : high]则为缩写 (*a)[low : high]。
对于 array 或字符串, 如果 <= <= <= , 则索引在范围内, 否则超出范围。对于 slice, 上限索引是 slice 的容量而不是长度。甲恒定索引必须是通过 type 的值是非负的和表示的 ; 对于 array 或常量字符串, 常数索引也必须在范围内。如果两个指标都不变, 则必须满足。如果索引在运行时超出范围, 则会发生运行时紧急。 0lowhighlen(a)cap(a)intlow <= high
除非类型字符串外, 如果 slice 操作数是字符串或片, 则 slice 操作的结果是与操作数相同类型的非常量值。对于无类型的字符串操作数, 结果是类型的非常量值 string。如果 slice 操作数是 array, 则它必须可寻址 , 并且 slice 操作的结果是与 array 具有相同元素类型的 slice。
如果有效 slice 表达式的 slice 操作数是 nilslice, 则结果为 nilslice。否则, 结果与操作数共享其底层 array。
** 全 slice 表达式 **
对于 array, 指向 array 或 slicea(而不是字符串)的指针, 是主表达式
a [low:high:max]
构造相同类型的 slice, 并且具有与简单 slice 表达式相同的长度和元素 a[low : high]。此外, 它通过设置它来控制结果 slice 的容量 max - low。只有第一个索引可以省略; 它默认为 0.slicearray 后 a
a:= [5] int {1,2,3,4,5}
t:= a [1:3:5]
slicet 具有类型[]int, 长度 2, 容量 4 和元素
t [0] == 2
t [1] == 3
对于简单的 slice 表达式, 如果 a 是指向 array 的指针, a[low : high : max]则为缩写(*a)[low : high : max]。如果 slice 操作数是 array, 则必须是可寻址的。
如果它们超出范围, 则 指数在范围内。甲恒定索引必须是通过 type 的值是非负的和表示的 ; 对于 array, 常数索引也必须在范围内。如果多个索引是常数, 则存在的常数必须在相对于彼此的范围内。如果索引在运行时超出范围, 则会发生运行时紧急。 0 <= low <= high <= max <= cap(a)int
** 断言 **
对于表达式 x 的接口类型 和类型 T, 主表达式
X.(T)
断言 x 不是 nil , 存储的值 x 是类型 T。该符号 x.(T)称为类型断言。
更精确地, 如果 T 不是一个接口类型, x.(T)断言, 动态型的 x 是相同 的类型 T。在这种情况下, T 必须实现 (接口) 类型 x; 否则类型断言是无效的, 因为不可能 x 存储类型的值 T。如果 T 是接口类型, 则 x.(T)断言实现 x 接口的动态类型 T。
如果类型断言成立, 表达式的值是存储的值 x, 其类型是 T。如果类型断言为 false, 则会发生运行时紧急情况。换句话说, 即使动态类型的 x 只是在运行时是已知的类型, x.(T)被称为是 T 在一个正确的程序。
var x interface {} = 7 // x 具有动态类型 int 和值 7
i:= x.(int) // 我有类型 int 和值 7
type I interface {m()}
func f(y I){
s:= y.(string)// illegal:string 不实现 I(缺少方法 m)
r:= y.(io.Reader)// r 具有类型 io.Reader,y 的动态类型必须实现 I 和 io.Reader
...
}
在特殊表单 的分配或初始化中 使用的类型断言
```n
v,ok = x.(T)
v,ok:= x.(T)
var v,ok = x.(T)
var v,ok T1 = x.(T)
产生一个额外的无类型布尔值。值 ok 是 true 如果断言成立。否则, 它是 false 和值 v 是零值的类型 T。在这种情况下不会发生运行时恐慌。
** 调用 **
给定一个 f 函数类型 的表达式 F,
f(a1,a2,... an)
调用 f 带参数 a1, a2, … an。除了一个特殊情况, 参数必须是可以分配给参数类型的单值表达式 , F 并在调用函数之前进行求值。表达式的类型是结果类型 F。方法调用是相似的, 但是方法本身被指定为方法的接收器类型的值的选择器。
math.Atan2(x,y)// 函数调用
var pt * Point
pt.Scale(3.5)// 方法调用接收器 pt
在函数调用中, 函数值和参数 按照通常的顺序进行计算。评估后, 调用的参数将通过值传递给函数, 被调用函数开始执行。当函数返回时, 函数的返回参数通过值传递回调用函数。
调用 nil 函数值会导致运行时的紧急情况。
作为特殊情况, 如果函数或方法的返回值 g 在数量上相等并且可以单独分配给另一个函数或方法的参数 f, 则在 将返回值绑定到顺序的参数后 , 调用将调用。调用必须不包含调用以外的参数, 并且必须至少有一个返回值。如果有一个最终参数, 那么它将被分配给定义参数赋值后的返回值。
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 failed")
}
x.m()如果方法集 (类型)x 包含 m 且参数列表可以分配给参数列表, 方法调用是有效的 m。如果 x 是寻址和 & x 的方法集包含 m,x.m()是简写(&x).m():
var p Point
p.Scale(3.5)
没有明确的方法类型, 没有方法文字。
** 变长参数传递 ...
参数 **
如果 f 是可变参数与最终的参数 p 类型的...T, 那么内 f 的类型 p 是相当于类型[]T。如果 f 没有实际参数被调用 p, 则传递给的 p 值为 nil。否则, 传递的值是类型的新 slice[]T 用新的底层 array, 其连续元素是实际的参数, 其中所有必须分配 到 T。因此, slice 的长度和容量是 p 每个呼叫站点绑定的参数数量, 并且可能不同。
给定功能和调用
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
在内部 Greeting,who 将有 nil 第一个调用的值 , []string{"Joe", "Anna", "Eileen"}在第二个。
如果最后一个参数可以分配给 slice 类型[]T, 则...T 如果参数后面跟着, 则它可以作为参数的值不变地传递...。在这种情况下, 不会创建新的 slice。
给予 slices 和调用
s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)
** 运算符优先 **
一元运算符具有最高优先级。由于 ++ 和 -- 运算符形成语句, 而不是表达式, 它们落在运算符层次结构之外。因此, 声明与之 * p++ 相同(*p)++。
二进制运算符有五个优先级。乘法运算符绑定最强, 后跟加法运算符, 比较运算符 &&(逻辑与)和最后 ||(逻辑或):
优先运算符
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
** 整数溢出 **
为无符号整数值, 所述操作 +, -,*, 和<<被计算模 2 Ñ, 其中 Ñ 为的比特宽度的无符号整数的类型。松散地说, 这些无符号整数运算在溢出时丢弃高位, 程序可能会依赖于 "环绕"。
对于有符号整数, 操作 +, -,*, 和<<可以合法溢出并且将所得值存在并且确定地由带符号整数, 操作, 和它的操作数定义。由于溢出而引起异常。在不发生溢出的假设下, 编译器可能无法优化代码。例如, 它可能不认为这 x < x + 1 是永远是真的。
** 浮点运算符 **
对于浮点数和复数, +x 是相同的 x, 而是 - x 否定的 x。没有超出 IEEE-754 标准规定浮点或零除零的结果。是否发生运行时恐慌 是实现特定的。
** 字符串连接 **
字符串可以使用 + 运算符或 += 赋值运算符进行连接:
s:="hi"+ string(c)
s + ="�and good bye"
字符串加法通过连接操作数创建一个新的字符串。
** 比较运算符 **
比较运算符比较两个操作数, 并产生一个无类型的布尔值。
== 相等
!= 不相等
<少
<= 小于或等于
更大
= 大于或等于
在任何比较中, 第一个操作数必须分配 给第二个操作数的类型, 反之亦然。
等式运算符 == 和!= 应用到操作数是可比的。排序运营商 <,<=,> 和>= 适用于那些操作数排序。这些术语和比较结果定义如下:
- 布尔值是可比的。如果他们或者都两个布尔值相等 true 或两者 false。
整数值可以和通常的方式进行排序。
浮点数值可以与 IEEE-754 标准定义相当且有序。
复杂的值是可比的。两个复数值 u 和 v 是如果两个相等的 real(u) == real(v)和 imag(u) == imag(v)。 - 字符串值是可比较的, 按字节顺序排序。
- 指针值是可比的。如果两个指针的值指向相同的变量或两者都具有值, 则两个指针值相等 nil。指向不同零大小变量的指针可能相同也可能不相等。
- 通道值是可比的。如果两个通道值是通过相同的调用创建的, make 或者两者都具有值, 则两个通道值相等 nil。
- 接口值是可比的。两个接口值相等, 如果它们具有相同的动态类型和等于动态值或如果两者都具有价值 nil。
- 当 x 类型 X 值 t 类似 T 且实现时, 非接口类型的值和接口类型的值 X 是可 X 比较的 T。如果 t 动态类型 X 与 t 动态值相同, 则动态值相等 x。
- 如果它们的所有领域都是可比较的, 则结构值是可比较的。如果相应的非空白字段相等, 则两个结构值相等。
- 如果 array 元素类型的值可比较, 则 array 值可比较。如果两个 array 的相应元素相等, 则两个 array 值相等。
具有相同动态类型的两个接口值的比较会导致运行时的紧急情况, 如果该类型的值不可比较。此行为不仅适用于直接接口值比较, 还适用于将接口值或结构体的 array 与接口值字段进行比较时。
slice, 地图和功能值不可比。然而, 作为特殊情况, 可以将片, 地图或函数值与预先声明的标识符进行比较 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
)
** 转换 **
转换是表单的表达式 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 在任何这些情况中:
** 与字符串类型的转换 **
将有符号或无符号整数值转换为字符串类型会产生包含整数的 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"
将字节 Slice 转换为字符串类型会产生一个字符串, 其连续字节是 Slice 的元素。
string([] byte {'h','e','l','l','\ xc3','\ xb8'})//"hellø"
string([] byte {})//""string([] byte(nil))//""
键入 MyBytes []字节
string(MyBytes {'h','e','l','l','\ xc3','\ xb8'})//"hellø"
将一段符号转换为字符串类型会产生一个字符串, 该字符串是转换为字符串的单个符文值的并置。
string([] rune {0x767d,0x9d6c,0x7fd4})//"\ u767d \ u9d6c \ u7fd4"=="白鹏翔"
string([] rune {})//""string([] rune(nil))//""
键入 MyRunes []符文
string(MyRunes {0x767d,0x9d6c,0x7fd4})//"\ u767d \ u9d6c \ u7fd4"=="白鹏翔"
将字符串类型的值转换为字节类型的 Slice 会产生一个 slice, 其连续元素是字符串的字节。
[] byte("hellø")// [] byte {'h','e','l','l','\ xc3','\ xb8'}
[] byte("")// [] byte {}
MyBytes("hellø")// [] byte {'h','e','l','l','\ xc3','\ xb8'}
将字符串类型的值转换为符号类型的 Slice 将生成包含字符串的各个 Unicode 代码点的 slice。
[] rune(MyString("白鹏翔"))// [] rune {0x767d,0x9d6c,0x7fd4}
[] rune("")// [] rune {}
MyRunes("白鹏翔")// [] rune {0x767d,0x9d6c,0x7fd4}
printFloat64(i)// 我的类型是 float64
case func(int)float64:
printFunction(i)// 我的类型是 func(int)float64
case bool,string:
printString("type is bool or string")// 我的类型是 x(interface {})
默认:
printString("不知道类型")// 我的类型是 x(interface {})
}
可以重写:
v:= x // x 被精确评估一次
if v == nil {
i:= v // 我的类型是 x(interface {})
printString("x is nil")
} else if i,isInt:= v.(int); isInt {
printInt(i)// 我的类型是 int
} else if i,isFloat64:= v.(float64); isFloat64 {
printFloat64(i)// 我的类型是 float64
} else if i,isFunc:= v.(func(int)float64); isFunc {
printFunction(i)// 我的类型是 func(int)float64
} else {
_,isBool:= v.(bool)
_,isString:= v.(string)
if isBool || isString {
i:= v // 我的类型是 x(interface {})
printString("type is bool or string")
} else {
i:= v // 我的类型是 x(interface {})
printString("不知道类型")
}
}
**Go 语句 **
A"走出去" 的语句开头的函数调用独立的控制并发线程, 或执行的 goroutine, 相同的地址空间内。
GoStmt ="go" 表达式。
表达式必须是函数或方法调用; 它不能括号。对于表达式语句, 内置函数的调用 受到限制。
函数值和参数 在调用 goroutine 中一样被评估, 但与常规调用不同, 程序执行不等待调用的函数完成。相反, 函数在新的 goroutine 中开始独立执行。当函数终止时, 其 goroutine 也终止。如果函数有任何返回值, 则在函数完成时将被丢弃。
去服务器()
去 func(ch chan < - bool){for {sleep(10); ch < - true; }} (C)
选择语句
"选择" 语句选择一组可能的 发送或 接收操作中的 哪一个将继续进行。它看起来类似于 "switch" 语句, 但是这些情况都是指通信操作。
SelectStmt ="select""{"{CommClause }"}"。
CommClause = CommCase ":" StatementList。
CommCase ="case"(SendStmt | RecvStmt)| "默认"。
RecvStmt = [ ExpressionList "="| IdentifierList ":="] RecvExpr。
RecvExpr = 表达式。
具有 RecvStmt 的情况可以将 RecvExpr 的结果分配给一个或两个变量, 这些变量可以使用短变量声明来声明 。RecvExpr 必须是 (可能是括号) 的接收操作。最多可以有一个默认情况, 它可能会出现在案例列表中的任何位置。
执行 "select" 语句可以在几个步骤中进行:
对于语句中的所有情况, 在输入 "select" 语句时, 接收操作的通道操作数和发送语句的通道和右侧表达式以源的顺序精确地计算一次。结果是一组要从中发送或发送的频道以及相应的值发送。无论选择哪个 (如果有的话) 通信操作进行, 该评估中的任何副作用都将发生。具有短变量声明或赋值的 RecvStmt 左侧的表达式尚未被评估。
如果一个或多个通信可以进行, 则可以通过统一的伪随机选择来选择可以进行的一个。否则, 如果有默认情况, 则选择该情况。如果没有默认情况, 则 "select" 语句将阻塞, 直到至少有一个通信可以继续。
除非选择的情况是默认情况, 否则执行相应的通信操作。
如果所选案例是具有短变量声明或赋值的 RecvStmt, 则将评估左侧表达式并分配接收的值(或值)。
执行所选案例的语句列表。
由于 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):// 相同: i3,ok:= <-c3
if ok{
print("received",i3,"from c3 \ n")
} else {
print("c3 已关闭 \ n")
}
case a [f()] = <-c4:
// 与... 一样:
// case t:= <-c4
// a [f()] = t
default:
print("no communication \ n")
}
{// 将随机的位序列发送到 c
select {
case c < - 0:// 注意: 没有声明, 没有掉落, 没有折叠的情况
case c < - 1:
}
}
选择{} // 阻止永远
回报表
函数中的 "返回" 语句 F 终止执行 F, 并可选地提供一个或多个结果值。任何延迟的函数 F 在 F 返回给其调用者之前执行。
ReturnStmt ="return"[ ExpressionList ]。
在没有结果类型的函数中,"return" 语句不能指定任何结果值。
func noResult(){
返回
}
有三种方法可以从结果类型的函数返回值:
返回值或值可以在 "return" 语句中明确列出。每个表达式必须是单值的, 并可分配 给函数结果类型的相应元素。
func simpleF()int {
返回 2
}
func complexF1()(re float64,im float64){
返回 - 7.0,-4.0
}
"return" 语句中的表达式列表可以是对多值函数的单个调用。效果就好像从该函数返回的每个值都分配给具有相应值的类型的临时变量, 后跟一个列出这些变量的 "return" 语句, 此时应用前一种情况的规则。
func complexF2()(re float64,im float64){
返回 complexF1()
}
如果函数的结果类型指定了其结果参数的名称, 则表达式列表可能为空。结果参数作为普通局部变量, 函数可以根据需要为其分配值。"return" 语句返回这些变量的值。
func complexF3()(re float64,im float64){
re = 7.0
im = 4.0
返回
}
func(devnull)Write(p [] byte)(n int,_ error){
n = len(p)
返回
}
无论如何声明它们, 所有结果值都将在输入函数时初始化为其类型的零值。指定结果的 "返回" 语句在执行任何延迟函数之前设置结果参数。
实现限制: 如果与结果参数具有相同名称的不同实体 (常量, 类型或变量) 在返回位置的范围内, 编译器可能会在 "返回" 语句中禁止空表达式列表 。
func f(n int)(res int,err error){
如果_,err:= f(n-1); err!= nil {
return // invalid return statement:err is shadowed
}
返回
}
打破声明
"break" 语句终止同一功能内最内层的 "for", "switch" 或 "select" 语句的执行。
BreakStmt ="break"[ Label ]。
如果有标签, 它必须是封闭的 "for","switch" 或 "select" 语句, 这是执行终止的标签。
OuterLoop:
对于 i = 0; 我 < n; 我 ++ {
对于 j = 0; j <m; j ++ {
切换[i] [j] {
案例无
状态 = 错误
打破 OuterLoop
病例项目:
状态 = 找到
打破 OuterLoop
}
}
}
继续发言
"继续" 语句在其 post 语句中开始下一次最内层 "for" 循环的迭代。"for" 循环必须在同一个函数内。
ContinueStmt ="continue"[ Label ]。
如果有一个标签, 它必须是一个封闭的 "for" 语句, 这就是执行进度的那个。
RowLoop:
对于 y,row:= range rows {
对于 x,data:= range row {
如果 data == endOfRow {
继续 RowLoop
}
row [x] = data + bias(x,y)
}
}
Goto 语句
"goto" 语句将控件转移到具有相同功能中的相应标签的语句。
GotoStmt ="goto" 标签。
goto 错误
执行 "GOTO" 语句不能造成任何变量生效 范围是不是已经在范围在跳转点。例如, 这个例子:
goto L // BAD
v = = 3
L:
是错误的, 因为跳到标签 L 跳过创建 v。
块 外的 "goto" 语句不能跳转到该块内的标签。例如, 这个例子:
如果 n%2 == 1 {
goto L1
}
对于 n> 0 {
F()
N--
L1:
F()
N--
}
是错误的, 因为标签 L1 在 "for" 语句的块内, 但 goto 不是。
落后声明
"fallthrough" 语句将控制转移到表达式 "switch" 语句中的下一个 case 子句的第一个语句。它可能只能用作这样一个子句中的最后一个非空的语句。
FallthroughStmt ="fallthrough"。
延迟声明
"延迟" 语句调用一个函数, 其执行延迟到周围函数返回的时刻, 这是因为周围的函数执行了一个返回语句, 到达其函数体的末尾, 或者因为相应的 goroutine 是惊慌。
DeferStmt ="延迟" 表达式。
表达式必须是函数或方法调用; 它不能括号。对于表达式语句, 内置函数的调用 受到限制。
每次执行 "延迟" 语句时, 调用的函数值和参数按照惯例进行 评估, 并重新保存, 但实际的函数不被调用。相反, 延迟函数在周围函数返回之前立即被调用, 按照相反的顺序被推迟。如果延迟函数值计算为 nil, 调用函数时执行紧急情况, 而不是执行 "延迟" 语句。
例如, 如果延迟函数是函数文字, 并且周围的函数具有范围在文字中的命名结果参数, 则延迟函数可以在返回结果参数之前访问和修改它们。如果延迟函数具有任何返回值, 则在函数完成时将被丢弃.(另见有关处理恐慌的部分)
锁(L)
延迟解锁(l)// 解锁在周围函数返回之前发生
// 打印 3 2 1 0, 然后循环返回
对于 i:= 0; 我<= 3; 我 ++ {
推迟 fmt.Print(i)
}
// f 返回 1
func f()(result int){
推迟 func(){
结果 ++
}()
返回 0
}
内置功能
内置函数是 预先声明的。它们被称为任何其他函数, 但其中一些函数接受一个类型而不是一个表达式作为第一个参数。
内置函数没有标准的 Go 类型, 因此它们只能出现在调用表达式中 ; 它们不能用作函数值。
关
对于通道 c, 内置函数 close(c) 记录在通道上不会再发送更多值。如果 c 是仅接收通道, 则是错误的。发送或关闭封闭的通道会导致运行时间的紧急。关闭 nil 通道也会导致运行时间的恐慌。调用 close 后, 在接收到任何先前发送的值之后, 接收操作将返回通道类型的零值, 而不会阻塞。多值接收操作 返回接收的值以及通道是否关闭的指示。
长度和容量
内置的函数 len 和 cap 各种类型的参数, 并返回类型的结果 int。实施保证结果总是适合 int。
调用参数类型结果
len(s)字符串类型字符串长度(以字节为单位)
[n] T,* [n] Tarray 长度(== n)
[] T 片长度
地图[K] T 地图长度(已定义键数)
chan T 在通道缓冲区中排队的元素数
cap(s)[n] T,* [n] Tarray 长度(== n)
[] Tslice 容量
chan T 通道缓冲容量
片的容量是在底层 array 中分配空间的元素数。在任何时候, 以下关系成立:
0 <= len(s)<= cap(s)
nilslice, 图或通道 的长度为 0. nilslice 或通道的容量为 0。
表达 len(s)是常数, 如果 s 是字符串常量。表达式 len(s)和 cap(s)常量, 如果类型 s 是 array 或指向 array 的指针, 并且表达式 s 不包含 通道接收或(非常量) 函数调用 ; 在这种情况下 s 不进行评估。否则, 调用 len 并且 cap 不是常量并被 s 评估。
const(
c1 = imag(2i)// imag(2i)= 2.0 是常数
c2 = len([10] float64 {2})// [10] float64 {2}不包含函数调用
c3 = len([10] float64 {c1})// [10] float64 {c1}不包含函数调用
c4 = len([10] float64 {imag(2i)})// imag(2i)是一个常量, 不发出函数调用
c5 = len([10] float64 {imag(z)})// invalid:imag(z)是一个 (非常数) 函数调用
)
var z complex128
** 分配 **
内置函数 new 采用类型 T, 在运行时为该类型的变量分配存储, 并返回 * T 指向该类型的类型的值。变量按初始值部分所述进行 初始化。
新(T)
例如
键入 S struct {a int; b float64}
新闻)
为类型的变量分配存储 S, 初始化它(a=0,b=0.0), 并返回包含 * S 位置的地址的类型的值。
制作 slice, 地图和频道
内置函数 make 采用一种类型 T, 它必须是片, 地图或通道类型, 可选地后跟一个特定于表达式的类型列表。它返回一个值类型 T(not *T)。按照关于初始值的章节所述初始化存储器 。
呼叫类型 T 结果
具有长度为 n 和容量 n 的类型 T 的(T,n)slice
具有长度为 n 和容量 m 的 T 型(T,n,m)slice
make(T)T 型地图
将 T(T,n)的地图映射为 n 个元素的初始空间
(T)通道无缓冲通道
(T,n)通道缓冲通道, 缓冲区大小 n
size 参数 n,m 必须是整型或非类型。一个常量大小的参数必须是非负数, 并可以通过类型的值表示 int。如果两个 n 和 m 提供和是不变的, 则 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)不能由 int 类型表示
s:= make([] int,10,0)// illegal:len(s)> cap(s)
c:= make(chan int,10)// 缓冲区大小为 10 的通道
m:= make(map [string] int,100)// 用 100 个元素的初始空间映射
附加和复制 slice
内置功能 append 并 copy 协助常用 slice 操作。对于这两个函数, 结果与参数引用的内存是否重叠无关。
所述可变参数函数 append 追加零个或多个值 x , 以 s 类型的 S, 它必须是一个 slice 类型, 并返回型也所得到的 slice,S。的值 x 被传递给类型的参数...T , 其中 T 为元素类型的 S 和相应的 参数传递规则适用。作为特殊情况, append 还可以接受可分配的第一个参数,[]byte 其中带有第二个参数的字符串类型...。此表单附加字符串的字节。
附加(s S,x ... T)S // T 是 S 的元素类型
如果容量 s 不够大以适应附加值, 则 append 分配一个新的, 足够大的底层 array, 适合现有的 slice 元素和附加值。否则, append 重新使用底层 array。
s0:= [] int {0,0}
s1:= append(s0,2)// 附加单个元素 s1 == [] int {0,0,2}
s2:= append(s1,3,5,7)// 附加多个元素 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 overlap 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 []字节
b = append(b,"bar"...)// append string contents b == [] byte {'b','a','r'}
该功能 copy 将 slice 元素从源复制 src 到目标 dst, 并返回复制的元素数。两个参数必须具有相同的元素类型 T, 并且必须可以 分配给类型的 slice[]T。复制的元素的数目是最小 len(src)和 len(dst)。作为一种特殊情况, copy 还可以接受一个目标参数, 该参数可分配给[]byte 一个字符串类型的源参数。此表单将字节中的字节复制到字节片中。
copy(dst,src [] T)int
copy(dst [] byte,src string)int
例子:
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
The built-in function delete removes the element with key k from a map m. The type of k must be assignable to the key type of m.
delete(m, k) // remove element m[k] from map m
If the map m is nil or the element m[k] does not exist, delete is a no-op.
Manipulating complex numbers
三个功能组合和拆卸复数。内置函数 complex 从浮点实部构造一个复数值, real 并 imag 提取复数值的实部和虚部。
complex(realPart,imaginaryPart floatT)complexT
real(complexT)floatT
imag(complexT)floatT
参数和返回值的类型对应。为 complex, 这两个参数必须是相同的浮点类型的和返回类型是复杂类型与相应的浮点成分: complex64 为 float32 参数, 并 complex128 为 float64 参数。如果其中一个参数的计算结果为无类型常数, 首先 转换到其他参数的类型。如果两个参数都计算为非类型常数, 则它们必须是非复数, 或者它们的虚部必须为零, 并且函数的返回值是无类型的复常数。
为 real 和 imag 时, 参数必须是复杂类型, 和返回类型是相应的浮点类型: float32 用于 complex64 参数, 并 float64 为一个 complex128 参数。如果参数计算为非类型常数, 则它必须是一个数字, 函数的返回值是无类型的浮点常量。
的 real 和 imag 功能一起形成的逆 complex, 所以对一个值 z 的复杂类型的 Z, z == Z(complex(real(z), imag(z)))。
如果这些函数的操作数都是常量, 则返回值是一个常数。
var a = complex(2,-2)// complex128
const b = complex(1.0,-1.4)// 无类型复数常数 1 - 1.4i
x:= float32(math.Cos(math.Pi / 2))// float32
var c64 = complex(5,-x)// complex64
var s uint = complex(1,0)// 无类型复数常数 1 + 0i 可以转换为 uint
_ = 复(1,2 << s)// 非法: 2 假定浮点型, 不能移位
var rl = real(c64)// float32
var im = imag(a)// float64
const c = imag(b)// 无类型常数 - 1.4
_ = imag(3 << s)// illegal:3 假设复杂类型, 不能移位
处理恐慌
两个内置函数, panic 并且 recover 有助于报告和处理运行时的恐慌 和程序定义的错误条件。
func panic(interface {})
func recover()interface {}
在执行函数 F 时, 显式调用 panic 或运行时的紧急 程序将终止执行 F。 随后执行任何延迟的 F 功能。接下来, 运行由 F's 调用者运行的任何延迟函数, 以及由执行 goroutine 中的顶级函数延迟的任何延迟函数。在这一点上, 程序被终止并且报告错误条件, 包括参数的值 panic。该终止序列称为恐慌。
```