当前位置: 首页 > news >正文

Go08-错误处理

第7章:错误处理

7.1 错误处理基础

Go 使用error接口处理错误,而非异常机制。

error 接口

// error 是一个内置接口typeerrorinterface{Error()string}// 创建错误err:=errors.New("something went wrong")fmt.Println(err)// something went wrong

惯用错误处理模式

// Go 的标准错误处理模式result,err:=doSomething()iferr!=nil{// 处理错误fmt.Println("错误:",err)returnerr}// 继续正常处理...

多返回值错误

funcdivide(a,bfloat64)(float64,error){ifb==0{return0,errors.New("除数不能为零")}returna/b,nil}// 使用result,err:=divide(10,0)iferr!=nil{fmt.Println("错误:",err)return}fmt.Println("结果:",result)

7.2 自定义错误

使用 errors.New

funcvalidateAge(ageint)error{ifage<0||age>150{returnerrors.New("年龄必须在 0-150 之间")}returnnil}

自定义错误类型

// 定义错误类型typeValidationErrorstruct{FieldstringMessagestring}func(e*ValidationError)Error()string{returnfmt.Sprintf("验证错误: 字段 %s, %s",e.Field,e.Message)}// 使用funcvalidateEmail(emailstring)error{ifemail==""{return&ValidationError{Field:"email",Message:"不能为空",}}if!strings.Contains(email,"@"){return&ValidationError{Field:"email",Message:"格式不正确",}}returnnil}// 处理自定义错误err:=validateEmail("invalid")iferr!=nil{varve*ValidationErroriferrors.As(err,&ve){fmt.Printf("字段: %s, 错误: %s\n",ve.Field,ve.Message)}}

错误包装

// Go 1.13+ 支持错误包装funcreadFile(filenamestring)([]byte,error){data,err:=os.ReadFile(filename)iferr!=nil{// 使用 fmt.Errorf 包装错误returnnil,fmt.Errorf("读取文件失败: %w",err)}returndata,nil}// 检查包装的错误err:=readFile("nonexistent.txt")iferr!=nil{// errors.Is 检查错误链iferrors.Is(err,os.ErrNotExist){fmt.Println("文件不存在")}// errors.As 提取特定错误类型varpathError*os.PathErroriferrors.As(err,&pathError){fmt.Println("路径错误:",pathError.Path)}}

7.3 错误处理最佳实践

1. 错误应该被处理

// 不推荐:忽略错误data,_:=os.ReadFile("file.txt")// 推荐:处理错误data,err:=os.ReadFile("file.txt")iferr!=nil{returnfmt.Errorf("读取文件失败: %w",err)}

2. 错误信息应该小写

// 不推荐returnerrors.New("Something Went Wrong")// 推荐returnerrors.New("something went wrong")

3. 错误应该提供上下文

// 不推荐returnerrors.New("failed")// 推荐returnfmt.Errorf("failed to read config file %s: %w",filename,err)

4. 使用哨兵错误

// 定义哨兵错误var(ErrNotFound=errors.New("not found")ErrUnauthorized=errors.New("unauthorized")ErrForbidden=errors.New("forbidden"))// 使用funcfindUser(idint)(*User,error){user:=db.FindByID(id)ifuser==nil{returnnil,ErrNotFound}returnuser,nil}// 检查user,err:=findUser(123)iferr!=nil{switch{caseerrors.Is(err,ErrNotFound):fmt.Println("用户不存在")caseerrors.Is(err,ErrUnauthorized):fmt.Println("未授权")default:fmt.Println("未知错误:",err)}}

7.4 panic 与 recover

panic

// panic 用于不可恢复的错误funcdivide(a,bint)int{ifb==0{panic("除数不能为零")}returna/b}// 捕获 panicfuncsafeDivide(a,bint)(resultint,errerror){deferfunc(){ifr:=recover();r!=nil{err=fmt.Errorf("panic: %v",r)}}()returndivide(a,b),nil}

recover

// recover 用于捕获 panicfuncmain(){deferfunc(){ifr:=recover();r!=nil{fmt.Println("捕获 panic:",r)}}()// 可能 panic 的代码panic("something bad happened")}

使用场景

// 1. HTTP 服务器中的 panic 恢复funcrecoveryMiddleware(next http.Handler)http.Handler{returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request){deferfunc(){iferr:=recover();err!=nil{log.Printf("panic: %v",err)http.Error(w,"Internal Server Error",500)}}()next.ServeHTTP(w,r)})}// 2. goroutine 中的 panic 恢复funcsafeGoroutine(){deferfunc(){ifr:=recover();r!=nil{log.Printf("goroutine panic: %v",r)}}()// goroutine 中的代码riskyOperation()}

7.5 错误处理模式

错误聚合

// 收集多个错误typeMultiErrorstruct{Errors[]error}func(me*MultiError)Error()string{varmsgs[]stringfor_,err:=rangeme.Errors{msgs=append(msgs,err.Error())}returnstrings.Join(msgs,"; ")}func(me*MultiError)Add(errerror){iferr!=nil{me.Errors=append(me.Errors,err)}}func(me*MultiError)HasErrors()bool{returnlen(me.Errors)>0}// 使用funcvalidateUser(user User)error{me:=&MultiError{}ifuser.Name==""{me.Add(errors.New("name is required"))}ifuser.Age<0{me.Add(errors.New("age must be positive"))}ifuser.Email==""{me.Add(errors.New("email is required"))}ifme.HasErrors(){returnme}returnnil}

错误重试

// 带重试的函数执行funcretry(attemptsint,sleep time.Duration,fnfunc()error)error{iferr:=fn();err!=nil{ifattempts--;attempts>0{time.Sleep(sleep)returnretry(attempts,sleep,fn)}returnerr}returnnil}// 使用err:=retry(3,time.Second,func()error{returndoSomething()})

7.6 练习

  1. 实现一个自定义的 HTTP 错误类型
  2. 编写一个函数,验证用户输入并返回所有验证错误
  3. 实现一个带重试机制的网络请求函数
  4. 编写一个 panic 恢复中间件

7.7 下一章

下一章我们将学习 Go 语言的并发编程。

http://www.jsqmd.com/news/1202524/

相关文章:

  • 如何通过CmdChallenge快速提升命令行效率?10个必试挑战详解
  • 集成电路制造核心工艺解析:从光刻到封装的技术要点
  • 武汉装修公司排行榜十大排名真的靠谱吗?意米设计靠37%转介绍率+85%还原度+0投诉脱颖而出 - 品牌红黑榜
  • CRM选型指南:从需求到落地,避开这5个坑就对了
  • TCP连接状态深度解析:从LISTENING到CLOSED的故障排查指南
  • 技术文档与代码注释规范:从谐音梗到工程实践的质量管控
  • PCB表面处理工艺全解析:从HASL到ENIG的选择指南
  • kvpress实战教程:用ExpectedAttentionPress压缩1M tokens上下文
  • sticky-js:打造终极粘性元素体验的轻量级Vanilla JS库
  • Windows本地安全策略与组策略实战:从零构建企业级安全基线
  • CmdChallenge高级玩法:自定义挑战与本地部署全攻略
  • 从零到一:在Mac上为VSCode构建高效的C++开发与调试环境
  • JAVA技术栈AI开发框架选型指南(2026)
  • 嵌入式传感器驱动开发深度解析——从 I2C/SPI 驱动到数据融合算法
  • 软测-测开面试题集合(二、实战场景与思维拓展篇)
  • OpenAI模型与Amazon Bedrock整合实战解析
  • DataRoom大屏设计器深度教程:从零到一构建专业级数据可视化
  • 为什么onedrive_user_enum是当前最可靠的Office 365用户枚举工具?
  • MediaInfoLib完全指南:轻松获取音视频文件的核心技术与标签数据
  • 我打造了一款开源飞控,基于FMUv5架构,专为APM固件深度优化-鹏心
  • Go09-并发编程
  • 2026年AI Agent市场从百花齐放到分层定局,选型逻辑变了:企业级智能体落地全景深度解析
  • 2026化工行业 GHS/CLP 国产标签打印软件合规打印方案
  • 搞大事掀桌子,万套授权免费送, AIGC工具迎来平权新选择!
  • Claude Code对接DeepSeek本地部署全链路指南
  • Linux服务器双向SSH免密登录配置与安全实践指南
  • atom-script的未来发展:路线图与功能规划展望
  • 如何提前规避高校 AI 审查?实测有效 AIGC 检测降重工具盘点
  • Pandas数据分析从入门到实战:58个核心知识点全面掌握
  • 公共阻抗耦合原理与电子系统抗干扰设计