Go语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在,例如:
package main
import "fmt"
func main(){
var j int=5
a:=func()(func()){
//圆括号中的func()表示返回值是一个func()函数
var i int=10
return func(){
//这里返回一个匿名函数
fmt.Printf("i,j: %v,%v\n",i,j)
}
}()//花括号后带参数列表表示调用匿名函数,执行到这里变量a就等于了一个函数了。
a()//调用函数a
j*=2//修改函数外部的变量j
a()//再次调用函数a
}
运行结果:
i, j: 10,5
i, j: 10,10
在上面的例子中,变量a指向的闭包函数引用了局部变量i和j,i的值被隔离,在闭包外不能被修改,改变j的值以后,再次调用a,发现结果是修改过的值。
在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问到,因此保证了i的安全性。
Go语言引入了一个关于错误处理的标准模式,即error接口,该接口的定义如下:
type error interface {
Error() string
}
创建error通常如下:
var e error=errors.New(“…”)//需要使用errors包
对于大多数函数,如果要返回错误,大致上都可以定义为如下模式,将 error作为多种返回值中的最后一个,但这并非是强制要求:
func Foo(param int)(res int,err error){
//....
}
调用时的代码建议按如下方式处理错误情况:
n, err :=Foo(0)
if err !=nil{
//错误处理
}else{
//使用返回值n
}
Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作是,遇到错误需要提前返回,在返回前你需要关闭相关的资源,不然很容易造成资源泄露等问题。
func ReadWrite() bool {
file.Open("file")
//做一些工作
if failureX {
file.Close()
return false
}
if failureY{
file.Close()
return false
}
file.Close()
return true
}
//我们看到上面有很多重复的代码,Go的defer有些解决了这个问题。使用它后。不但代码量减少了很多,而且程序变得更优雅。
func ReadWrite()bool{
file.Open("file")
defer file.Close()//保证资源正常关闭
if failureX {
return false
}
if failureY{
return false
}
return true
}
//如果有很多调用defer,那么defer是采用先进后出模式
for i:=0;i<5;i++{
defer fmt.Printf("%d",i)//输出结果:43210
}
defer有点类似java中的try{}finall{}
Go语言有2个内置的函数panic()和recover(),用以报告和捕获运行时发生的错误程序,与error不同,
panic和recover一般用在函数内部。一定要注意不要滥用panic和recover,可能会导致性能问题,
一般只在为之输入和不可靠请求时使用。
Go语言的错误处理流程:当一个函数在执行过程中出现了异常或遇到panic(),正常语句就会立刻终止,然后执行defer语句,再报告异常信息,最后退出goroutine.如果在defer中使用了recover()函数,则会捕获错误信息,使该错误信息终止报告。如下所示,例子来自网络
package main
import(
"log" //log包
"strconv" //字符转换包
)
//捕获因未知输入导致的程序异常
func catch(nums ...init) int {
defer func() {
//recover()可以捕获运行时发生的异常,避免异常时程序直接over,通常用在defer函数内
if r:=recover(): r !=nil{
log.Println("[E]",r)//将捕获的异常信息通过log打印,而不会导致挂掉
}
}()
return nums[1] *nums[2] *nums[3]//index out of range
}
//主动抛出panic,不推荐使用,可能会导致性能问题
func toFloat64(num string)(float64,error){
defer func(){
if r:=recover(): r !=nil{
log.Println("[W]",r)
}
}()
if num == "" {
panic("param is null")//主动抛出panic
}
return strconv.ParseFloat(num,10)
}
func main(){
catch(2,8)
toFloat64("")
}
运行结果:
2016/03/26 20:16:03 [E] runtime error: index out of range
2016/03/26 20:16:03 [W] param is null
golang的nil在概念上和其他语言的null,None,Null一样,都指代零值或空值。
nil是预先说明的标示符,也即通常意义上的关键字。在golang中,nil只能赋值给指针,channel,func,interface,map或slice类型的变量。如果未遵循这个规则,则会引发panic