04Golang的Error vs Exception

Go语言的错误处理不同于其他语言,它不引入Exception,而是使用Error接口。错误通常通过errors.New()创建,多返回值设计使得调用者必须检查error。Go中的panic类似于致命错误,用于不可恢复的情况,而正常错误应使用error处理。编程时应考虑失败情况,保持控制流清晰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Error

Go error就是一个普通的接口,普通的值。

http://golang.org/pkg/builtin/#error

type error interface {
    Error() string
}

我们常用一个errors.New()来返回一个error对象。

http://golang.org/src/pkg/errors/errors.go

type errorString struct  {
    s string
}
http://golang.org/src/pkg/errors/errors.go

func (e *errorString) Error() string {
    return e.s
}

基础库中大量自定义的error

http://golang.org/src/pkg/bufio/bufio.go

var (
    ErrInvalidUnreadByte = errors.New("Bufio: invalid use of UnreadByte")
    ...
)

errors.New()返回的是内部errorString对象的指针

http://golang.org/src/pkg/errors/errors.go

// 每次返回一个新的对象,取其地址,防止两次error new同样text时两次对象相等
func New(text string) error {
    return &errorString{text}
}

例子:

package main

import (
	"errors"
    "fmt"
)

// create a named type for our new type.
type errorString string

// implement the error interface.
func (e errorString) Error() string {
    return string(e)
}

// new creates interface values of type error.
func New(text string) error {
    return errorString{text}
}

var ErrNamedType = New("EOF")		// 自己构建的一种errorString的方法
var ErrStructType = errors.New("EOF")	// 标准库的方法返回的指针

func main() {
    // 自定义返回的对象
    if ErrNamedType == New("EOF") {
        fmt.Println("Named type Error")
    }
    
    // 重新用标准库返回的对象,在栈上new了两个指针,指向的内存地址肯定是不一样的
    if ErrStructType == errors.New("EOF") {
        fmt.Println("Struct type Error")
    }
}
Output:
Named Type Error

例子:

package main

import "fmt"

// create a named type for our new type.
type errorString struct {
	s string
}

// implement the error interface.
func (e errorString) Error() string {
	return e.s
}

func NewError(text string) error {
	return errorString{text}		// 与标准库区别的是此处返回的是值类型,不是指针类型
}

var ErrType = NewError("EOF")

func main() {
	if ErrType == NewError("EOF") {
		fmt.Println("Error:", ErrType)
	}
}
Output:
Error: EOF
因为在返回值不取地址的时候,会展开返回值的s字段做一个等值对比

Error vs Exception

各个语言的演进历史:

  • C

    单返回值,一般通过传递指针作为入参,返回值为int表示成功还是失败。

  • C++

    引入了exception,但是还无法知道被调用方会抛出什么异常。

  • Java

    引入了checked exception,方法的所有者必须申明,调用者必须处理。在启动时抛出大量的异常时很常见的,并在他们的调用堆栈中尽职地记录下来。Java异常不再是异常,而变成了司空见惯,他们从良性到灾难性都有使用,异常的严重性由函数调用者来区分。

Go异常

Go的处理异常逻辑是不引入exception,支持多参数返回,所以使用者很容易在函数签名中带伤实现了error interface的对象,交由调用者来判定。

如果一个函数返回了(value, error),你不能对这个value做任何假设,必须先判定error。唯一可以忽略error的是,若你连value也不关心。

Go也有panic机制,和其他语言的exception却不是相同的。当使用者抛出异常时,相当于使用者把exception扔给了调用者来处理。

比如在C++中,把string转换为int,如果转换失败,会抛出异常。

Go panic意味着fatal error(就是挂了)。不能假设调用者来解决panic,意味着代码不能继续运行了。

使用多个返回值和一个简单的约定,Go解决了让程序员知道什么时候除了问题,并为真正的异常情况保留了panic。

package main

import "fmt"

func handle() (int, error) {
    return 1, nil
}

func main() {
    i, err := handle()
    if err != nil {
        return
    }
    fmt.Println(i)
}

例子:

package main

import "fmt"

func Positive(n int) (bool, bool) {
	if n == 0 {
		return false, false
	}
	return n > -1, true
}

// 打印正数还是负数
func Check(n int) {
	pos, ok := Positive(n)
	if !ok {
		fmt.Println(n, "is neither")
		return
	}
	if pos {
		fmt.Println(n, "is positive")
	} else {
		fmt.Println(n, "is negative")
	}
}

Output:
1 is positive
0 is neither
-1 is negative

演进版本:

package main

import (
	"errors"
	"fmt"
)

func Positive(n int) (bool, error) {
	if n == 0 {
		return false, errors.New("undefined")
	}
	return n > -1, nil
}

// 打印正数还是负数
func Check(n int) {
	pos, err := Positive(n)
	if err != nil {
		fmt.Println(n, err)
		return
	}
	if pos {
		fmt.Println(n, "is positive")
	} else {
		fmt.Println(n, "is negative")
	}
}

func main() {
	Check(1)
	Check(0)		// 0是既不是正数也不是负数
	Check(-1)
}

Output:
1 is positive
0 undefined
-1 is negative

不建议的写法:

package main

import (
	"fmt"
)
// 使用panic和recover来模拟类似try-catch这种写法
// In the case that n is 0, Positive will panic
func Positive(n int) bool {
	if n == 0 {
		panic("indefined")
	}
	return n > -1
}

// 打印正数还是负数
func Check(n int) {
	defer func() {
		if recover() != nil {
			fmt.Println("is neither")
		}
	}()

	if Positive(n) {
		fmt.Println(n, "is positive")
	} else {
		fmt.Println(n, "is negative")
	}
}

func main() {
	Check(1)
	Check(0)		// 0是既不是正数也不是负数
	Check(-1)
}

Go的panic处理出现了fatal error情况下使用,比如除以0值、数组越界、不可恢复的环境问题、栈溢出。而对于其他的错误情况,我们应该是期望使用error来进行判定。

you onle need to check the error value if you care about the result. --Dave

  • 简单
  • 考虑失败,而不是成功(plan for failure, not success)
  • 没有隐藏的控制流
  • 完全交给使用者控制error
  • Error are values
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值