Go 语言 在线

2223Go 错误处理

异常处理

Go语言追求简洁优雅 所以 不支持传统的 try…catch…finally 这种异常

Go语言的设计者认为 将异常与控制结构混在一起 使 代码变得混乱

因为开发者 容易滥用异常 甚至一个小小的错误都抛出一个异常

 

 

Go语言 用多值返回 来返回错误 不用异常代替错误 更不要用来 控制流程

在极个别的情况下 才使用Go中引入的 Exception 处理 defer panic recover

panic

1 内建函数

2 假如函数F 书写 panic语句 会终止其后要执行的代码 在panic所在函数F内 如果存在要执行的 defer函数列表 按照 defer 逆序执行

3 返回函数F的调用者G 在G中 调用函数F语句之后的代码不会执行 假如函数G中存在要执行的defer函数列表 按照defer的逆序执行 这里的 defer 有点类似 try-catch-finally 中的 finally

4 直到goroutine整个退出 并报告错误

recover

1 内建函数

2 用来控制一个 goroutine 的 panicking 行为 捕获 panic 从而影响应用的行为

3 一般的调用建议

a) 在defer函数中 通过 recever 来终止一个 gojroutine 的 panicking 过程 从而恢复正常代码的执行

b) 可以获取通过 panic 传递的 error

简单 讲

Go语言 可以抛出一个 panic 异常 然后在 defer 中通过 recover 捕获这个异常 然后正常处理

示例

main 函数相当于调用者 G

f函数相当于函数F

func main() {

   fmt.Println("c")

   defer func() { // 必须要先声明 defer 否则不能捕获到 panic 异常

      fmt.Println("d")

      if err := recover(); err != nil {

         fmt.Println(err) // 这里的 err 其实 是 panic 传入的内容

      }

      fmt.Println("e")

   }()

   f() //开始调用f

   fmt.Println("f") //这里开始下面代码不会再执行

}

func f() {

   fmt.Println("a")

   panic("异常信息")

   fmt.Println("b") //这里开始下面代码不会再执行

}

-------output-------

c

a

d

异常信息

e

利用 recover 处理 panic 指令 defer 必须在 panic 之前声明 否则当 panic 时 recover 无法捕获到 panic

package main

import (

    "fmt"

)

func main() {

    defer func() {

        fmt.Println("1")

    }()

    defer func() {

        if err := recover(); err != nil {

            fmt.Println(err)

        }

    }()

    panic("fault")

    fmt.Println("2")

}

 

运行结果

fault

1

程序首先运行 panic 出现故障 跳转到包含 recover() 的 defer 函数执行 recover 捕获 panic 此时 panic 就不继续传递

但是 recover 后 程序并 不 返回到 panic 点继续执行以后的动作 而是在 recover 这个点继续执行以后的动作 即执行上面的 defer 函数 输出 1

利用 recover 处理 panic 指令 必须利用 defer 在 panic 之前声明 否则当 panic 时 recover 无法捕获到 panic 无法防止 panic 扩散

2222Go 错误处理

回收处理

GO 中 defer 代码块 在函数调用链表中 增加一个函数调用 这个函数调用不是普通的函数调用 是会在函数正常返回 也就是return 后添加一个函数调用 因此 defer通常用来释放函数内部变量

示例

func CopyFile(dstName, srcName string) (written int64, err error) {

src, err := os.Open(srcName)

if err != nil {

return

}

defer src.Close()

dst, err := os.Create(dstName)

if err != nil {

return

}

defer dst.Close()

return io.Copy(dst, src)

}

通过 defer 在代码中 关闭/清理 代码 所 用 变量 defer作为golang清理变量的特性 有 独有且明确的行为

defer 三条使用规则

当defer被声明时 其参数就会被实时解析 即defer的参数的变量值 在代码中defer使用后的位置改变并不会对改变defer用到的值

defer执行顺序为先进后出 在函数中 先定义的defer将会后执行

defer可以读取有名返回值

defer代码块的作用域仍然在函数之内 因此defer仍然可以读取函数内的变量

2215Go 语言 select 语句

Go 语言 Select 与 for 合 用时可能 的坑

func test(){

    i := 0

    for {

        select {

        case <-time.After(time.Second * time.Duration(2)):

            i++

            if i == 5{

                fmt.Println("break now")

                break

            }

            fmt.Println("inside the select: ")

        }

        fmt.Println("inside the for: ")

    }

}

执行后发现 居然break不出去 当for 和 select结合使用时 break语言是无法跳出for 的 因此 要break出来  需要加一个标签 使用 goto  或者 break 到具体的位置

解决方法一 使用golang中break的特性 在外层for加一个标签

func test(){

    i := 0

    ForEnd:

    for {

        select {

        case <-time.After(time.Second * time.Duration(2)):

            i++

            if i == 5{

                fmt.Println("break now")

                break ForEnd

            }

            fmt.Println("inside the select: ")

        }

        fmt.Println("inside the for: ")

    }

}

解决方法二 使用goto直接跳出循环

func test(){

    i := 0

    for {

        select {

        case <-time.After(time.Second * time.Duration(2)):

            i++

            if i == 5{

                fmt.Println("break now")

                goto ForEnd

            }

            fmt.Println("inside the select: ")

        }

        fmt.Println("inside the for: ")

    }

    ForEnd:

}

 成功走出 坑

2207Go 语言问题集

go get golang.org/x 包失败解决方法

国内使用 go get 安装 golang 官方包 失败

解决方法

golang 在 github 上

镜像库

https://github.com/golang/net

https://golang.org/x/net

镜像库

获取 golang.org/x/net 包 需要以下步骤

命令行里依次输入

mkdir -p $GOPATH/src/golang.org/x

cd $GOPATH/src/golang.org/x

git clone https://github.com/golang/net.git

其它 golang.org/x 下的包获取皆可使用该方法

很多go的软件在编译时使用tools

使用下面方法获取

git clone https://github.com/golang/tools.git

一定要保持与go get获取的目录结构是一致的

否则库就找不到

2206Go 语言问题集

把握生命里每一次感动 和心爱的朋友热情相拥