错误处理


• 错误处理的标准模式

golang错误处理的标准模式:error接口。

golang函数如果要返回错误,规范上是将error作为多返回值中的最后一个,但这并非是强制要求。

▶ error接口

type error interface {
Error() string
}

▶ 内置的error类型使用

▪ 语法如下

func 函数名(参数列表) (返回值列表, err error) {
// 函数体
}

▪ 错误处理

例如我们有一个这样的函数:

func Foo(param int) (n int, err error) {
// 函数体
}

调用函数时建议按如下方式处理错误:

n, err := Foo()
if err != nil {
// 错误处理
} else {
// 使用返回值n
}

▪ 示例如下

package main

import (
"errors"
"fmt"
) func divide(dividend float64, divisor float64) (result float64, err error) {
if divisor == {
return -, errors.New("除数为0")
} return dividend / divisor, nil
} func main() {
result, err := divide(, )
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("result =", result)
} result, err = divide(, )
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("result =", result)
}
}

▶ 自定义error类型使用

golang错误处理支持自定义的error类型,只需要为自定义error类型实现Error接口即可。

▪ 语法如下

type CustomError struct {
...
} func (e *CustomError) Error() string {
// 函数体
}

▪ 示例如下

package main

import (
"fmt"
) type MathError struct {
Op string
info string
} func (e *MathError) Error() string {
return "Math operation " + e.Op + " error : " + e.info
} func divide(dividend float64, divisor float64) (result float64, err error) {
if divisor == {
return -, &MathError{"division", "divisor is zero"}
} return dividend / divisor, nil
} func main() {
result, err := divide(, )
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("result =", result)
} result, err = divide(, )
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("result =", result)
}
}

▪ 类型转换

如果处理错误时需要获取详细信息,而不仅仅满足于打印一句错误信息,那就需要用到类型转换。

package main

import (
"fmt"
) type MathError struct {
Op string
info string
} func (e *MathError) Error() string {
return "Math operation " + e.Op + " error : " + e.info
} func divide(dividend float64, divisor float64) (result float64, err error) {
if divisor == {
return -, &MathError{"division", "divisor is zero"}
} return dividend / divisor, nil
} func main() {
result, err := divide(, )
if err != nil {
// error类型转换为*MathError指针,因为接口定义传入类型对象为*MathError指针
// 如果接口定义时传入类型对象为MathError,那么这里的写法为err.(MathError)
if e, ok := err.(*MathError); ok {
fmt.Println(e.info)
}
} else {
fmt.Println("result =", result)
}
}

资源释放


在c++程序中,经常要注意内存指针、文件句柄、网络套接字等等资源的释放,特别需要注意其释放的时机。而golang使用defer
关键字和背后的内部机制简单地解决了资源释放的问题。

defer关键字能保证其后的代码能在函数退出前调用。

一个函数中可以存在多个defer语句,需要注意的是defer语句的调用是遵照先进后出的原则,即最后一个defer语句将最先被执行

可以在defer后加一个匿名函数来进行复杂的清理工作。

• 简单的清理工作

▶ 语法如下

func 函数名(参数列表) (返回值列表) {
...
// 资源申请
defer 清理函数
...
}

▶ 示例如下

package main

import (
"io"
"os"
) func CopyFile(dst, src string) (w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
defer srcFile.Close() dstFile, err := os.Create(dst)
if err != nil {
return
}
defer dstFile.Close() return io.Copy(dstFile, srcFile)
} func main() {
CopyFile("D:/2.txt", "D:/1.txt")
}

▶ 先进后出规则

package main

import (
"fmt"
) func Test() {
defer fmt.Println()
defer fmt.Println()
defer fmt.Println()
} func main() {
Test()
}

• 复杂的清理工作

▶ 语法如下

func 函数名(参数列表) (返回值列表) {
...
// 资源申请
defer func() {
// 复杂的清理工作
} ()
...
}

▶ 示例如下

package main

import (
"fmt"
"io"
"os"
) func CopyFile(dst, src string) (w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
defer func() {
fmt.Println("close file :", src)
srcFile.Close()
}() dstFile, err := os.Create(dst)
if err != nil {
return
}
defer func() {
fmt.Println("close file :", dst)
dstFile.Close()
}() return io.Copy(dstFile, srcFile)
} func main() {
CopyFile("D:/2.txt", "D:/1.txt")
}

异常处理


一些高级语言中一般提供类似try...catch...finally...的语法,用于捕获异常。golang提供panicrecover两个关键字用于异常处理。

• panic

panic在golang中是一个内置函数,接收一个interface{}类型的值作为参数:

func panic(interface{}) {
...
}

当一个函数执行过程中调用panic函数时,函数执行流程将立即终止,但panic之前的defer关键字延迟执行的语句将正常执行,之后该函数将返回到上层调用函数,并逐层向上执行panic流程,直至函数所属的goroutine中所有正在执行函数终止。错误信息将被报告,包括在调用panic()函数时传入的参数。下面用一个示例说明:

package main

import (
"fmt"
) func MyFunc1() {
defer fmt.Println("MyFunc1 defer 1") panic("MyFunc1 panic test") defer fmt.Println("MyFunc1 defer 2")
} func MyFunc2() {
defer fmt.Println("MyFunc2 defer 1") MyFunc1() defer fmt.Println("MyFunc2 defer 2")
} func main() {
MyFunc2()
}

程序输出如下:

• recover

recover在golang中是一个内置函数,返回一个interface{}类型的值作为参数:

func recover() interface{} {
...
}

panic函数触发后不会立即返回,而是先defer,再返回。如果defer的时候,有办法将panic捕获到,然后及时进行异常处理,并阻止panic传递,那处理机制就完善了。因此golang提供了recover内置函数,用于捕获panic并阻止其向上传递。需要注意的是,recover之后,逻辑并不会恢复到panic处,函数还是会在defer之后返回,但是所属goroutine将不会退出。

▶ 本层函数处理

package main

import (
"fmt"
) func MyFunc1() {
defer func() {
fmt.Println("MyFunc1 defer 1")
if r := recover(); r != nil {
fmt.Println("Runtime error caught :", r)
}
}() panic("MyFunc1 panic test") fmt.Println("MyFunc1 defer 2")
} func MyFunc2() {
defer fmt.Println("MyFunc2 defer 1") MyFunc1() defer fmt.Println("MyFunc2 defer 2")
} func main() {
MyFunc2()
}

程序输出如下:

▶ 上层函数处理

package main

import (
"fmt"
) func MyFunc1() {
defer fmt.Println("MyFunc1 defer 1") panic("MyFunc1 panic test") fmt.Println("MyFunc1 defer 2")
} func MyFunc2() {
defer func() {
fmt.Println("MyFunc1 defer 2")
if r := recover(); r != nil {
fmt.Println("Runtime error caught :", r)
}
}() MyFunc1() defer fmt.Println("MyFunc2 defer 2")
} func main() {
MyFunc2()
}

程序输出如下:

• 模拟try...catch...语法

▶ 语法如下

func Try(f func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}() f()
}

▶ 示例如下

package main

import (
"fmt"
) func Try(f func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}() f()
} func main() {
Try(func() {
panic("main panic")
}, func(e interface{}) {
fmt.Println(e)
})
}

[golang note] 错误处理的更多相关文章

  1. 个人犯的一个golang routine错误

    这个其实不是错误,2个写法没有区别.-2015.11.22 认识golang也不少时间了,也做过几个项目.最近发现之前用golang写的一个服务,内存涨得比较快,一直没找出来原因来.今天把疑惑发到群里 ...

  2. [golang note] 环境搭建

    LiteIDE(windows) • golang安装 ▶ 下载对应操作系统的版本并安装,下载地址:http://www.golangtc.com/download,譬如这里下载的是go1.6.win ...

  3. [golang note] 网络编程 - RPC编程

    net包 • 官方文档 http://godoc.golangtc.com/pkg/net/ Package net provides a portable interface for network ...

  4. [golang note] 接口使用

    侵入式接口 √ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约. √ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承. √ 如 ...

  5. [golang note] 匿名组合

    匿名组合 golang也提供了继承机制,但采用组合的文法,因此称为匿名组合.与其他语言不同, golang很清晰地展示出类的内存布局是怎样的. • 非指针方式组合 ▶  基本语法 // 基类 type ...

  6. [golang note] 函数定义

    普通函数定义 √ golang函数基本组成:关键字func.函数名.参数列表.返回值.函数体和返回语句. • 语法如下 func 函数名(参数列表) (返回值列表) { // 函数体 } • 示例如下 ...

  7. [golang note] 内建类型

    基础类型 √ golang内建基础类型有布尔类型.整数类型.浮点类型.复数类型.字符串类型.字符类型和错误类型. 复合类型 √ golang支持的复合类型有指针.数组.数组切片.字典.通道.结构体和接 ...

  8. [golang note] 数组切片

    数组 √ golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). √ golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量 ...

  9. [golang note] 变量常量

    变量 • 变量声明 √ golang变量声明的关键字为var. √ golang变量声明时类型信息放置在变量名之后. ▶ 单个变量声明 ▪ 语法如下 var name type ▪ 示例如下 var ...

随机推荐

  1. IoC最大的好处是什么

    IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有 ...

  2. OGNL支持各种纷繁复杂的表达式

    OGNL支持各种纷繁复杂的表达式.但是最最基本的表达式的原型,是将对象的引用值用点串联起来,从左到右,每一次表达式计算返回的结果成为当前对象,后面部分接着在当前对象上进行计算,一直到全部表达式计算完成 ...

  3. 判断站点访问的终端类型(移动端还是pc端)的方法(转)

    要了解某个网站是在移动设备上打开的还是在pc web浏览器中打开的,我们可以有以下综合的几种方式来搞定: 通过判断Request.UserAgent中的具体信息来分析判断,因为UserAgent包含了 ...

  4. ubuntu安装wineqq遇到错误

    在安装中发生了错误:Errors were encountered while processing: wine-qqintl 原因是还有lib没有配置,所以再输入sudo apt-get insta ...

  5. mysql 分页sql

    -- pageSize=3 pageIndex total=7 -- 开始行号 startRowNum=(pageIndex-1)*pageSize+1; -- 结束行号 endRowNum=page ...

  6. 如今就报名上海ORACLE用户组2014年高峰论坛吧!

    COLLABORATE 14 – SHOUG Forum 上海ORACLE用户组2014年高峰论坛报名本次活动由ORACLE ACS高级服务部门与 SHOUG 上海ORACLE用户组合办. 大会议程包 ...

  7. MFC WinInetHttp抓取网页代码内容

    Windows Internet编程主要包括两方面: l  服务器端   l  客户端 WinInet编程 Internet客户端主要实现的功能,主要是通过Internet协议(HTTP.FTP等)获 ...

  8. Struts2_day02--Struts2封装获取表单数据方式

    Struts2封装获取表单数据方式 原始方式获取表单封装到实体类对象 属性封装(会用) 1 直接把表单提交属性封装到action的属性里面 2 实现步骤 (1)在action成员变量位置定义变量 - ...

  9. Integer.parseInt()和Integer.valueOf()方法详解

    1.Integer.parseInt(): public static int parseInt(String s) throws NumberFormatException { return par ...

  10. 引入外部文件的时候为什么省略http:

    [摘要]相信很多时候大家在使用网上的实例代码的时候,需要引入外部文件,然而它们通常都是省略了http:,这是为什么呢? 先给大家看一个简单的小例子,引入外部jquery文件: <script s ...