Go语言中,错误被认为是一种可以预期的结果;而异常则是一种非预期的结果,发生异常可能表示程序中存在 BUG 或发生了其它不可控的问题。
错误用error接口类型表示,我们可以通过实现该接口来自定义异常信息,并通过Error()方法来获得字符串类型的错误信息。
package main
import (
"fmt"
)
type MyError struct {
Code uint32
Message string
}
func NewMyError(code uint32, msg string) error {
return &MyError{
Code: code,
Message: msg,
}
}
// GetCode 获取错误码
func (e *MyError) GetCode() uint32 {
return e.Code
}
// GetMessage 获取错误信息
func (e *MyError) GetMessage() string {
return e.Message
}
// MyError 实现了 Error() 方法的对象都可以
func (e *MyError) Error() string {
return fmt.Sprintf("%d-%s", e.Code, e.Message)
}
func NameCheck(name string) (bool, error) {
if name == "" {
return false, NewMyError(100, "name is empty")
}
return true, nil
}
func main() {
_, err := NameCheck("")
if err != nil {
// 判断是否自定义错误类型,如果是自定义类型则输出code:message
e, ok := err.(*MyError)
if ok && e != nil {
fmt.Println(e.GetCode(), ":", e.GetMessage())
} else {
fmt.Println(err)
}
} else {
fmt.Println("name is ok")
}
}
// out: 100 : name is empty
package main
import (
"errors"
"fmt"
)
func NameCheck(name string) (bool, error) {
if name == "" {
return false, errors.New("第一层错误")
}
return true, nil
}
func main() {
_, err := NameCheck("")
if err != nil {
// 使用fmt.Errorf("xxxx %w", err)来进行错误wrap
err = fmt.Errorf("第二层错误: %w", err)
// Unwrap 一次只解一层错误,所以可以通过遍历来判断一共wrap几层
for ; err != nil; err = errors.Unwrap(err) {
fmt.Println(err)
}
} else {
fmt.Println("name is ok")
}
}
// out:
// 第二层错误: 第一层错误
// 第一层错误
// errors.Is(err, target error) bool
// 如果err和target是同一个,那么返回true
// 如果err是一个wraperror,target也包含在这个嵌套error链中的话,那么也返回true。
package main
import (
"errors"
"fmt"
)
type MyError struct {
msg string
err error
}
func (e *MyError) Error() string {
return e.msg
}
func (e *MyError) Unwrap() error {
return e.err
}
func main() {
e1 := &MyError{"第1层错误", nil}
e2 := &MyError{"第2层错误", e1}
e3 := &MyError{"第3层错误", e2}
err := &MyError{"第3层错误", e2}
// err == err
fmt.Println("is err:", errors.Is(err, err))
// err 包装了e1, 返回true
fmt.Println("is e1:", errors.Is(err, e1))
// err 包装了e2, 返回true
fmt.Println("is e2:", errors.Is(err, e2))
// err 未包装了e3, 返回false
fmt.Println("is e3:", errors.Is(err, e3))
}
// errors.As(err, target error) bool
// 如果err和target是同一个,那么返回true, 并将值赋给target
// 如果err是一个wraperror,target也包含在这个嵌套error链中的话,那么也返回true, 并将值赋给target
package main
import (
"errors"
"fmt"
)
type MyError struct {
msg string
err error
}
func (e *MyError) Error() string {
return e.msg
}
func (e *MyError) Unwrap() error {
return e.err
}
type MyNewError struct {
msg string
err error
}
func (e *MyNewError) Error() string {
return e.msg
}
func (e *MyNewError) Unwrap() error {
return e.err
}
func main() {
e1 := &MyError{"第1层错误", nil}
err := &MyNewError{"第3层错误", e1}
var aE1 *MyError
fmt.Println("as MyError:", errors.As(err, &aE1))
fmt.Println(aE1)
var aE2 *MyNewError
fmt.Println("as MyNewError:", errors.As(err, &aE2))
fmt.Println(aE2)
}
// out:
// as MyError: true
// 第1层错误
// as MyNewError: true
// 第3层错误
Go 语言中的错误是一种接口类型。接口信息中包含了原始类型和原始的值,只有当接口的类型和原始的值都为空的时候,接口的值才对应 nil。
在下面的例子中,试图返回自定义的错误类型,并且当没有错误的时候返回 nil,但是最终返回的结果其实并非是nil, 而是一个正常的错误,错误的值是一个 MyError 类型的空指针。
func returnsError() error {
var p *MyError = nil
if bad() {
p = ErrBad
}
return p // Will always return a non-nil error.
}
修正returnsError:
func returnsError() error {
if bad() {
return (*MyError)(err)
}
return nil
}
因此,在处理错误返回值的时候,没有错误的返回值最好直接写为 nil。
panic异常可以通过 recover进行捕获,让程序恢复正常。
注意:
// 正确示例:数据越界, 可以捕获异常
package main
import (
"fmt"
)
func testPanicError(){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var bar = []int{1}
fmt.Println(bar[1])
}
func main(){
testPanicError()
fmt.Println("exit")
}
// out
// runtime error: index out of range
// exit
// 错误示例:使用嵌套defer, 不可以捕获异常
package main
import (
"fmt"
)
func testPanicError(){
defer func() {
defer func() {
// 无法捕获异常
if err := recover(); err != nil {
fmt.Println(err)
}
}()
}()
var bar = []int{1}
fmt.Println(bar[1])
}
func main(){
testPanicError()
fmt.Println("exit")
}
// 错误示例:使用封装的recover, 不可以捕获异常
func main() {
defer func() {
// 无法捕获异常
if r := MyRecover(); r != nil {
fmt.Println(r)
}
}()
panic(1)
}
func MyRecover() interface{} {
log.Println("trace...")
return recover()
}
// 错误示例:跨goroutine, 不可以捕获异常
package main
import (
"fmt"
"time"
)
func testPanicError() {
var bar = []int{1}
fmt.Println(bar[1])
}
func main() {
defer func() {
// 无法捕获goroutine中异常
if err := recover(); err != nil {
fmt.Println("main panic", err)
}
}()
go testPanicError()
fmt.Println("exit")
time.Sleep(time.Second * 5)
}
// out:
exit
panic: runtime error: index out of range [1] with length 1
goroutine 18 [running]:
main.testPanicError()
xxx/err/main.go:10 +0x1b
created by main.main
xxx/err/main.go:20 +0x46
Process finished with the exit code 2
不可以通过recover进行捕获,程序直接退出os.Exit(1)
// log中fatal
// Fatal is equivalent to Print() followed by a call to os.Exit(1).
func Fatal(v ...any) {
std.Output(2, fmt.Sprint(v...))
os.Exit(1)
}
// src/runtime/panic.go:func fatal(s string)
func fatal(s string) {
// Everything fatal does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
systemstack(func() {
print("fatal error: ", s, "\n")
})
fatalthrow(throwTypeUser)
}
不可以通过recover进行捕获,程序直接退出os.Exit(1)
// 并发写map
package main
import (
"fmt"
)
func testThrowError(){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var bar = make(map[int]int)
go func(){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
}
}()
for{
_ = bar[1]
}
}()
for{
bar[1]=1
}
}
func main(){
testThrowError()
fmt.Println("exit")
}
// out:
fatal error: concurrent map read and map write
goroutine 5 [running]:
runtime.throw(0x4bd8b0, 0x21)
xx/src/runtime/panic.go:617 +0x72 fp=0xc00004c780 sp=0xc00004c750 pc=0x427f22
runtime.mapaccess1_fast64(0x49eaa0, 0xc000088180, 0x1, 0xc0000260d8)
xx/src/runtime/map_fast64.go:21 +0x1a8 fp=0xc00004c7a8 sp=0xc00004c780 pc=0x40eb58
......
exit status 2
// 这里主要是map并发写会触发throw,这里需要加锁处理
if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
}
// src/runtime/panic.go:func throw(s string)
func throw(s string) {
// Everything throw does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
systemstack(func() {
print("fatal error: ", s, "\n")
})
fatalthrow(throwTypeRuntime)
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有