转载

深入Go语言 - 9

本章介绍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、 complexrealimag : 复数操作
10、 panicrecover : 报告panic和处理panic,后面讲
11、 printprintln : 尽量不用这两个函数,因为保证将来它们还会留在Go语言中,使用fmt.Print、fmt.Println

Go的代码文件中都会有包(package)的定义,在import声明的前面。

同一个文件夹下的所有的文件都要使用同一个包名(当然,你如果单独编译每一个文件的话,可以不遵守,但是要编译整个项目,必须遵守)。

但是测试文件可以叫另外的包名,比如正常代码的包名为"package abc",测试代码和示例代码的包名为"package abc_test". Go标准库中混用了这两种风格。

main 包是一个特殊的包,必须声明一个main函数,main函数无参数,无返回值,它用来创建可执行程序。

包名不一定和文件夹的名字保持一致,经常我们的项目的名称很长,不太适合做包名,所以包名可以用一个简短的名称,但是如果可能,尽量用一样的名字,这样在下载库的时候就能直到包的名字了。

import

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函数实现其它的初始化过程,你可以显示地定义这个函数,它可以出现在同一个包下的多个文件中,执行的顺序由编译器决定。如果包导入了其它包,则导入的包会先初始化。如果多个包都导入同一个包,则导入的包只被初始化一次。

error、panic和recover

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 } 
原文  http://colobu.com/2016/06/29/dive-into-go-9/
正文到此结束
Loading...