golang Error的一些坑

golang Error的一些坑

golang error的设计可能是被人吐槽最多的golang设计了。

最经典的err!=nil只影响代码风格设计,而有一些坑会导致我们的程序发生一些与我们预期不符的问题,开发过程中需要注意。

image

errors.Is​判断error是否Wrap不符合预期

errors.Is​经常用于判断当前的的error(第一个参数)是否包含target error(第二个参数),用于替代==​判断当前err与target err的关系,like:

const badInput = "abc"

var ErrBadInput = errors.New("bad input")

func validateInput(input string) error {
    if input == badInput {
        return fmt.Errorf("validateInput: %w", ErrBadInput)
    }
    return nil
}

func main() {
    input := badInput

    err := validateInput(input)
    if errors.Is(err, ErrBadInput) {
        fmt.Println("bad input error")
    }
}

结果输出:bad input error​ ,符合预期。

但是我们再来看看下面代码:

func main() {
	errMsg := "123"
	fmt.Println(errors.Is(errors.New(errMsg), errors.New(errMsg))) //结果反直觉,结果为false

	err123 := errors.New(errMsg)
	fmt.Println(errors.Is(err123, err123)) //结果为true
}

我们使用相同的errMsg初始化了两个err,使用errors.Is​比较,结果为false。

原因是errors.Is​虽然可以不断的拆包unwarp,但是在不能继续unwarp的时候,会用err == Error 直接比较错误对象与指定的错误对象,也就是指针的对比,肯定就对比不通过了。
这样很容易踩坑。

虽然erros.Is是会自动帮unwarp的,但是底层本质上还是指针的对比,new出来的err肯定是不同的err。

经验教训:

  • 不要使用erros.New的方式来判断error是否相等或包含,如果非要使用就用error()把error信息打出来来比较吧。

  • 为什么常能看见库设计者是直接实例化了一个public​的error对象来提供给你使用,而不是将其设置为private让你自己去new一个来判断(设计成private之后使用者就必须使用errors.Err()打印出msg来判断是否相等了,实在太蠢),like:

image

如果不了解erros.Is的使用可以参考:https://gosamples.dev/check-error-type/

自定义error判断是否为nil不符合预期

见代码:

type CustomizedError struct {
}

func (c CustomizedError) Error() string {
	return "CustomizedError"
}

func func1() error {
	var err *CustomizedError = nil
	return err
}

func main() {
	err := func1()
	if err != nil {
		log.Fatalf("1 err: %v", err) // 不符合预期,以为 err 为nil,但是会进这个分支
	}
}

上面判断不符合预期的原因是error类型是一个接口Interface,interface 设计为了两部分:

  • type
  • value

其中,value 由一个任意的具体值表示,称作 interface 的 dynamic value ;而 type 则对应该 value 的类型(即 dynamic type);例如对于对于 var a int = 3来说,把a 赋值给interface时, interface是使用(int, 3)进行存储的

因此当想判断 interface 的值为 nil​时 ,则必须是其内部 value 和 type 均未设置的情况,即 (nil, nil)​ ;

在上面的代码案例中func1()​中返回值err的type已经不是nil了,因此后续会判断不通过。

禁言教训:

  • 自定义error的时候禁止在任何地方出现dynamic value为nil的error,like:var err *CustomizedError = nil​,要么直接返回nil,要么初始化一个不为nil的返回。
  • 延续上一条:如果自己是库的提供者,因此避免暴露自定义的error类型,而是只提供NewMyError() error函数,避免使用方产生误用。like:

type myErr struct { // 注意这里的设计,myErr没有暴露出去
   code int
   msg  string
}

func (e myErr) Error() string {   
   return fmt.Sprintf("code:%d,msg:%v", e.code, e.msg)
}

func New(code int, msg string) error {// 注意这里的设计,myErr没有暴露出去,只提供一个返回error的初始化方法
   return myErr{
      code: code,
      msg:  msg,
   }
}

func GetCode(err error) int {
   if e, ok := err.(myErr); ok {
      return e.code
   }
   return -1
}

func GetMsg(err error) string {
   if e, ok := err.(myErr); ok {
      return e.msg
   }
   return ""
}

参考资料:

https://coolshell.cn/articles/21140.html

https://juejin.cn/post/6974037920567017509

<think>好的,我现在需要处理用户关于Golang错误信息或解决方案的查询。首先,用户希望得到与Golang相关的错误处理和解决方案的信息。根据用户提供的引用内容,我需要从中提取相关的信息并结合自己的知识来组织回答。 首先,用户提供的引用中有几处提到Golang相关的代码和功能。引用[1]中的代码展示了在Go中更新数据库的函数,可能涉及错误处理,比如使用`err = problem.UpdateByMap(param)`,这里可能返回错误,需要处理。引用[2]是关于使用Golang实现Web服务器的例子,其中涉及HTTP请求和错误处理,比如`client.Do(req)`可能返回错误,需要检查`err`。引用[3]和[4]提到了Golang的编译和工具链,比如`go get`命令可能出现的错误。引用[5]涉及安装依赖时的错误,比如`pip install jsonlines`可能的问题,但这里需要注意用户可能混淆了Go和Python的包管理。 接下来,我需要确定常见的Golang错误类型,并根据引用中的内容提供对应的解决方案。例如,空指针解引用、协程泄露、接口类型断言失败、HTTP请求错误、跨平台编译错误以及依赖管理问题等。对于每种错误,需要结合引用中的示例代码,说明错误原因和解决方法。 例如,在引用[2]的HTTP请求例子中,错误处理通过检查`err != nil`来捕获错误,并在出现错误时使用`log.Fatalln(err)`记录并退出。这可以作为一个典型的错误处理模式,提示用户检查网络连接或服务器状态。此外,在引用[1]中的数据库更新函数中,错误处理通过返回`err`,用户需要确保传入的参数正确,比如`modelName`的有效性,避免无效类型导致的错误。 还需要注意引用[4]提到的`git-codereview`工具,安装时可能出现的依赖或路径问题,建议用户检查环境变量或版本兼容性。引用[5]中的`pip install jsonlines`可能属于Python的包管理,需要指出用户可能误用了包管理器,应使用Go的模块管理。 最后,组织这些问题和解决方案,确保结构清晰,每个错误类型有对应的例子和解决方法,并引用相关的引用来源。同时,生成相关问题,引导用户进一步探索,比如错误处理的最佳实践或调试工具的使用。 需要验证每个解决方案是否正确,例如空指针解引用确实可以通过检查变量初始化来避免,协程泄露需要通过`defer`或`WaitGroup`来处理。同时,确保引用的代码示例正确对应所提到的错误类型,比如HTTP请求中的错误处理确实在引用[2]中出现过。 总结步骤: 1. 分类常见Golang错误类型。 2. 为每个类型提供原因和解决方案。 3. 结合用户提供的引用中的代码示例。 4. 确保引用标识正确添加。 5. 生成相关问题,促进用户深入理解。</think>### Golang错误处理与解决方案 #### 1. 空指针解引用(nil pointer dereference) **原因**:尝试访问未初始化对象的方法或属性。 **示例**: ```go var p *Problem p.UpdateByMap(param) // panic: runtime error ``` **解决方案**: - 初始化对象:`p := &Problem{ID: id}` - 添加nil检查: ```go if p != nil { p.UpdateByMap(param) } ``` 引用[1]中`var problem = &model.Problem{ID: id}`即通过显式初始化避免空指针[^1]。 --- #### 2. 协程泄露(goroutine leak) **原因**:未正确管理协程生命周期,导致资源未释放。 **解决方案**: - 使用`defer`或`sync.WaitGroup`同步协程: ```go var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() // 业务逻辑 }() wg.Wait() ``` --- #### 3. 接口类型断言失败 **原因**:断言类型与实际类型不匹配。 **示例**: ```go var data interface{} = "hello" value := data.(int) // panic ``` **解决方案**: - 使用安全断言: ```go if value, ok := data.(int); ok { // 安全操作 } ``` --- #### 4. HTTP请求错误处理 **原因**:网络问题、无效URL或服务器无响应。 **示例代码**(引用[2]): ```go resp, err := client.Do(req) if err != nil { log.Fatalln(err) // 记录错误并终止 } ``` **解决方案**: - 检查网络连接和URL有效性。 - 添加重试逻辑: ```go retries := 3 for i := 0; i < retries; i++ { resp, err = client.Do(req) if err == nil { break } } ``` --- #### 5. 跨平台编译错误 **原因**:未正确设置`GOOS`或`GOARCH`。 **解决方案**: - 明确指定目标平台: ```bash GOOS=linux GOARCH=amd64 go build -o app ``` 引用[3]提到GOS工具可简化跨平台编译流程[^3]。 --- #### 6. 依赖管理问题 **错误示例**:`go get`安装失败或版本冲突。 **解决方案**: - 使用Go Modules: ```bash go mod init project go mod tidy ``` 引用[4]中的`go get -u golang.org/x/review/git-codereview`需确保网络代理配置正确[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值