go的错误处理、测试、文档
与其说是教程,更像是总结
一起学习
错误处理
error内置接口
package main
import (
"errors"
"fmt"
"math"
)
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("同学,这是负数呀!")
} else {
return math.Sqrt(f), nil
}
}
func main() {
//异常情况
res := math.Sqrt(-100)
fmt.Println(res) //NaN,也就是Not-a-Number,非数,不是数。NaN不是一个非数,而是一类非数。
//使用自己定义的异常错误,直接打印错误信息
res, err := Sqrt(-100)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
//err.Error(),输出错误信息
res, err = Sqrt(-10)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(res)
}
}
NaN
同学,这是负数呀!
同学,这是负数呀!
error对象
创建error对象
- errors.New
err1 := errors.New("XXX")
fmt.Println(err1.Error())
fmt.Println(err1)
- Errorf 本质还是errors.New(xxx)
err2 := fmt.Errorf("XXX%d",1)
fmt.Println(err2.Error())
fmt.Println(err2)
Errorf源码
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// If the format specifier includes a %w verb with an error operand,
// the returned error will implement an Unwrap method returning the operand. It is
// invalid to include more than one %w verb or to supply it with an operand
// that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.
func Errorf(format string, a ...interface{}) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p.wrappedErr == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
自定义错误
自定义错误结构体
type MyError struct {
When time.Time
What string
}
实现Error()接口
func (e MyError) Error() string {
return fmt.Sprintf("%v : %v", e.When, e.What)
}
测试函数
func Sqrt(f float64) (float64, error) {
errorInfo := ""
if f < 0 {
errorInfo = fmt.Sprintf("不能为负数 %v ", f)
}
if errorInfo != "" {
return 0, MyError{time.Now(), errorInfo}
} else {
return math.Sqrt(f), nil
}
}
main入口
func main() {
sqrt, err := Sqrt(-100)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(sqrt)
}
}
2021-10-14 16:13:17.6144098 +0800 CST m=+0.003169801 : 不能为负数 -100
defer
- 确保调用在函数结束时发生
- 参数在defer语句时计算
- defer列表为后进先出
/**
1.defer 会在函数结束前输出,return,panic执行之前
2.如果多出defer.会按照栈排列输出,先进后出
*/
func tryDefer() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
}
func main() {
tryDefer()
}
3
2
1
利用defei实现字符串倒序
func main() {
str := "利用defer实现字符串倒序"
fmt.Println(str)
ReverseString(str)
}
func ReverseString(str string) {
for _, c := range []rune(str) {
defer fmt.Printf("%c", c)
}
}
利用defer实现字符串倒序
序倒串符字现实refed用利
panic
最严重的错误,程序会直接结束(如果没有recover)
Recover(宕机恢复)
panic 和 recover 的组合有如下特性:
- 有 panic 没 recover,程序宕机。
- 有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。
recover需要在defer中使用
func testFunc(){
defer func(){
msg := recover();
fmt.Println("获取recover的返回值: ",msg)
}()
fmt.Println("panic 前")
panic("调用panic")
}
panic 前
获取recover的返回值: 调用panic
错误案例
/**
@Go version: 1.17.6
@project: TeachCode
@ide: GoLand
@file: errorTest.go
@author: Lido
@time: 2022-02-20 22:55
@description:
*/
package main
import (
"errors"
"fmt"
)
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
return arg + 3, nil
}
type argError struct {
arg int
prob string
}
func (e argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
return -1, argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
_, e := f2(42)
/*
如果你想在程序中使用自定义错误类型的数据,
你需要通过类型断言来得到这个自定义错误类型的实例。
*/
if ae, ok := e.(argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}
f1 worked: 10
f1 failed: can't work with 42
f2 worked: 10
f2 failed: 42 - can't work with it
42
can't work with it
代码测试
不要调式,要好的测试
需要测试的函数
func calcTriangle(a, b int) int {
var c int
c = int(math.Sqrt(float64(a*a + b*b)))
return c
}
创建测试文件
必须要以xxx_test.go的格式 !
traingle_test.go
测试函数
测试函数必须以TestXxxx开头 !
package main
import "testing"
func Test_Triangle(t *testing.T) {
tests := []struct{ a, b, c int }{
{3, 4, 5},
{5, 12, 13},
{8, 15, 17},
{12, 35, 37},
{30000, 40000, 50000},
}
for _, tt := range tests {
if actual := calcTriangle(tt.a, tt.b); actual != tt.c {
t.Errorf("calcTriangle(%d, %d); "+
"got %d; expected %d",
tt.a, tt.b, actual, tt.c)
}
}
}
允许测试
//先cd 到文件目录下
go test .
代码覆盖率
//生成检测结果
go test -coverprofile=c.out
//查看检测结果
go tool cover -html=c.out
代码性能测试
func BenchmarkSubStr(b *testing.B) {
a := 3
b := 4
ans := 5
for i := 0; i < b.N; i++ {
actual := calcTriangle(a,b)
if actual != ans {
b.Errorf("got %d for input %s; "+
"expected %d",
actual, s, ans)
}
}
}
go test -bench .
检查数组数值异常
go run -race xxx.go
GO语言文档和示例代码
工具 go doc
go doc xxx
go doc Queue
工具 godoc
如果没有,安装
go get golang.org/x/tools/cmd/godoc
使用 -http 来查看文档
godoc -http :6060
编写文档示例
1.创建测试文件
2.编写测试函数
package queue
import (
"fmt"
)
func ExampleQueue_Pop() {
q := Queue{1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
q.Push("abc")
fmt.Println(q.Pop())
// Output:
// 1
// 2
// false
// 3
// true
// abc
}
再次打开文档
godoc -http :6060
PREVIOUS第六节 Go面向对象
NEXT第八节 Go 常见的内置包和IO