Go Spec

307 阅读6分钟

Types 类型

array types

长度是数组类型的一部分。用len函数可以求出数组的长度。数组类型是一维的,但可以构造多维。

slice types

SliceType = "[" "]" ElementType .

分片一旦初始化便始终关联到存放其元素的底层数组。因此分片会与其数组和相同数组的其它分片共享存储区;相比之下,不同的数组总是代表不同的存储区域。

interface Types

interface 指定了一组method。一个接口变量,可以存有这个接口中所有方法的 任何类型的值。

这个类型,就叫实现了这个接口。 未初始化的接口变量是nil

InterfaceType      = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
MethodSpec         = MethodName Signature .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

一个类型的method的子集包括的接口,这个类型就实现了这个接口。因此任何类型都实现了空接口 interface{}

接口中的方法可以 显示的声明,也可以通过另一个接口类型,嵌入。最终的接口方法是其中所有方法的并集。如果有多个同名的method,签名必须完全相同。

接口不可以嵌入自己,也不可以循环嵌入

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

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

Declarations ,Scope 声明和范围

Method 方法声明

Method就是 function + Receiver。

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

T 叫做 接受者Base Type,base Type不能是指针或interface,而且必须在和method相同的package下定义着。这个method和这个base type绑定了,只在T或者*T下可见。

Receiver中的 identifier 如果没有在方法中使用,可以省略。

Given defined type Point, the declarations

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
}

bind the methods Length and Scale, with receiver type *Point, to the base type Point.

The type of a method is the type of a function with the receiver as first argument. For instance, the method Scale has type

func(p *Point, factor float64)

However, a function declared this way is not a method.

Package 包

Go programs are constructed by linking together packages. A package in turn is constructed from one or more source files that together declare constants, types, variables and functions belonging to the package and which are accessible in all files of the same package. Those elements may be exported and used in another package.

Package声明

package声明在文件的开始,定义这个文件属于哪个包

PackageClause  = "package" PackageName .
PackageName    = identifier .

例如

package math

一个包🈶️相同包名的一个或多个文件构成,这些文件在同一个目录。

Import declarations

导入声明 说明该文件以来导入的包中的功能,而且可以访问 这些包中导出的identifiers.

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

PackageName是文件scope,可以省略,默认会是导入包的包名。

如果PackageName的位置是一个 . 那么,这个包里的导出的identifier都会导入到本文件中。

Import 声明方式           Sin的调用方式

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

如果导入一个包,并没有使用,是无法编译通过的。 如果只是需要导入这个包的副作用,需要把PackageName定为 _ . 就是导入并且忽略的意思。

Function 声明 declarations

函数声明就是把一个 函数名字,绑定到一个函数上。

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

函数声明可以没有body,这个body在go的外部实现,如build-in的一些函数。

程序初始化和执行

zero值

当给一个变量分类了存储,又没有明显的初始化值,就会给一个默认值的零值。

package 初始化

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

通过下面例子,可以看到package 初始化的顺序,是按照依赖的,而不是在源文件中的顺序, 初始化的顺序是 dbca.

var (
	a = c + b  // == 9
	b = f()    // == 4
	c = f()    // == 5
	d = 3      // == 5 after initialization has finished
)

func f() int {
	d++
	return d
}

初始化函数,package level, 没有参数,没有返回值

func init() { … }

init方法,不能被其他地方调用。

package 初始化的顺序是

1,初始化所有的 package level变量,赋值

2,执行init方法

如果有import,导入的包先于自己初始化。被导入的package只会初始化一次。导入者来保证没有循环依赖。

程序执行起点

程序的执行起点是 package main的 main方法。 最先初始化main package,然后调用main方法,当main方法返回时,程序就退出了。而且不会等待非main goroutines的结束。

Type 类型

Struct Type

struct就是一列有名字的元素,叫做field(字段),每一个都有一个名字和一个类型。 名字可以显示的给出来,也可以不显示的给出(这叫做EmbeddedField, 嵌入的字段) 在struct中,每一个非空的field名字必须要唯一。

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag           = string_lit .
// An empty struct.
struct {}

// A struct with 6 fields.
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()
}

一个字段只有类型,没有显示的给名字,叫做embedded field。这种字段必须是 类型名T或者 指针,指向非空的接口类型名 *T, T自己不能是指针类型。 类型名作为字段名。

// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
	T1        // field name is T1
	*T2       // field name is T2
	P.T3      // field name is T3
	*P.T4     // field name is T4
	x, y int  // field names are x and y
}

上面P是 package的意思。

下面的声明就是非法的,因为他们的字段名都会是T,因此就冲突了。

struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

字段可以有一个 可选的tag,tag是用来说明field属性的, tag可以被 反射接口看到。 其他场景是 被忽略的。

struct {
	x, y float64 ""  // an empty tag string is like an absent tag
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

Pointer 类型

未初始化的指针是nil,

PointerType = "*" BaseType .
BaseType    = Type .
*Point
*[4]int

Built-in functions

Allocation

内置函数 new,分配内存,初始化,返回该类型的指针

new(T)

For instance

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

Make slice, map, channel

内置函数 make, 只能用于 slice,map或者channel。返回的是类型T,而不是*T

Call             Type T     Result

make(T, n)       slice      slice of type T with length n and capacity n
make(T, n, m)    slice      slice of type T with length n and capacity m

make(T)          map        map of type T
make(T, n)       map        map of type T with initial space for approximately n elements

make(T)          channel    unbuffered channel of type T
make(T, n)       channel    buffered channel of type T, buffer size n

Composite literals

符号 ... 指定一个数组长度等于其最大元素索引加1。

days := [...]string{"Sat", "Sun"}  // len(days) == 2