本章介绍Go语言的其它部分,算是Go语言规范的深入学习的收尾工作。
前面几章我们已经认识了几个内建的函数,这里我们将所有的内建函数罗列一下。
如果你查看它们的定义的话,你可以访问 builtin/builtin.go ,但这些内建的函数只有声明,没有方法体,它们不是标准的Go类型,你不能把它们作为函数值进行参数传递,只能出现在调用表达式中。事实上这些内建类型并不真的在builtin包中,只是为了生成文档的需要。
你可以在任意的包中调用这些内建函数,不必引入特定的包如"builtin"。
1、 close : 关闭channel
2、 len(s) :得到字符串、数组、数值指针、slice、map、chan的长度
3、 cap(s) :得到数组、数组指针的长度,得到slice、channel的容量
4、 new(T) : 生成类型T的零值指针,注意它返回的是指针 *T
5、 make : 生成slice、map、channel对象
调用 类型 T 结果 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,初始可以容纳 n 元素的空间 make(T) channel 不带缓存的channel of type T,比如我们在声明信号channel的时候 make(T, n) channel 带缓存的 channel of type T, 缓存大小为 n
6、 append(s S, x ...T) S : 增加0到n个元素到slice中,返回新的slice
7、 copy(dst, src []T) int : 复制源src slice的元素到目标dst slice中,返回复制的元素的数量n, n是src和dst长度的最小值。字符串也可以作为src,但是T的类型必须是byte
8、 delete(m,k) : 删除map中的一个映射, m为nil或者m[k]不存在也不会panic,而是一个空操作
9、 complex 、 real 、 imag : 复数操作
10、 panic 、 recover : 报告panic和处理panic,后面讲
11、 print 、 println : 尽量不用这两个函数,因为保证将来它们还会留在Go语言中,使用fmt.Print、fmt.Println
Go的代码文件中都会有包(package)的定义,在import声明的前面。
同一个文件夹下的所有的文件都要使用同一个包名(当然,你如果单独编译每一个文件的话,可以不遵守,但是要编译整个项目,必须遵守)。
但是测试文件可以叫另外的包名,比如正常代码的包名为"package abc",测试代码和示例代码的包名为"package abc_test". Go标准库中混用了这两种风格。
main
包是一个特殊的包,必须声明一个main函数,main函数无参数,无返回值,它用来创建可执行程序。
包名不一定和文件夹的名字保持一致,经常我们的项目的名称很长,不太适合做包名,所以包名可以用一个简短的名称,但是如果可能,尽量用一样的名字,这样在下载库的时候就能直到包的名字了。
import用来引入所需要的类型,允许你访问另外的包中的导出类型。
以下四种形式都是可以接受的mport:
Import declaration Local name of Sin import "lib/math" math.Sin import m "lib/math" m.Sin import . "lib/math" Sin import _ "lib/math"
为了避免同名的包名冲突,你可以为导入的包名起一个名字,比如上例中的"m"。
你也可以使用 .
,这样无需包名标识符,可以直接使用这个包下的导出类型。
最后一个情况是使用空标识符,主要是想利用包的初始化,而不是使用它的导出类型。
import不能导入包自己,不管是直接的还是间接的(循环引用)。你也不能直接导入一个包而不使用它的导出类型,所幸一些工具可以自动帮我们修正import的错误,或者自动帮我们导入,比如 goimports 。
import可以导入相对路径,如"import /"../foo/bar/"",但是强烈你不要这么做,这不是常用的导入风格。
import可以用小括号括起来导入多个包。
包变量的初始化顺序和它们声明的顺序一致,但是也得考虑它们的依赖。循环依赖初始化也不可以:
packageabc variint= j varjint= k varkint= i
包变量的初始化之后可以调用一个init函数实现其它的初始化过程,你可以显示地定义这个函数,它可以出现在同一个包下的多个文件中,执行的顺序由编译器决定。如果包导入了其它包,则导入的包会先初始化。如果多个包都导入同一个包,则导入的包只被初始化一次。
Go预定义了 error
类型,虽然它的首字母不是大写的,但是确可以在任何包下使用:
typeerrorinterface{ Error() string }
而errors包定义了生成简单Error的方法 errors.New(text String) error
。如果你想自定义Error类型,你可以实现error接口。
运行时Error,比如数组索引越界会触发一个运行时的panic,它等价调用panic函数,panic的值是一个runtime.Error:
packageruntime typeErrorinterface{ error // and perhaps other methods }
而panic的处理是在一个defer函数中执行的:
funcmain() { deferfunc() { fmt.Println("foo") }() deferfunc() { ifx :=recover(); x !=nil{ fmt.Println(x) } }() deferfunc() { fmt.Println("bar") }() panic("trigger panic") fmt.Println("end") }
注意defer的执行顺序,前面已经讲过,和它们声明的顺序相反,所以输出结果为:
bar trigger panic foo
最后一句没有机会继续执行,因为recover执行完后函数就终止了。这带来一个问题,如果函数有返回值,recover后函数的返回值是什么?
funcmain() { i := z() fmt.Println(i) } funcz()int{ deferfunc() { fmt.Println("foo") }() deferfunc() { ifx :=recover(); x !=nil{ fmt.Println(x) } }() deferfunc() { fmt.Println("bar") }() panic("trigger panic") fmt.Println("end") return100 }
输出结果是:
bar trigger panic foo 0
可以函数的返回是返回类型的零值。
当然说零值也不完全正确,如果函数有命名的返回参数,并且命名的返回参数在panic之前赋值了的话,返回的结果还是最后的赋值结果,下面的代码中函数的返回结果为50:
funcmain() { i := z() fmt.Println(i) } funcz() (rint) { deferfunc() { fmt.Println("foo") }() deferfunc() { ifx :=recover(); x !=nil{ fmt.Println(x) } }() deferfunc() { fmt.Println("bar") }() r =50 panic("trigger panic") fmt.Println("end") return100 }