第5章:Go语言错误处理和异常
5.1 错误类型基础
5.1.1 error接口
type error interface {
Error ( ) string
}
type CustomError struct {
Message string
Code int
}
func ( e * CustomError) Error ( ) string {
return fmt. Sprintf ( "错误码: %d, 信息: %s" , e. Code, e. Message)
}
5.1.2 创建和使用错误
func divide ( a, b int ) ( int , error ) {
if b == 0 {
return 0 , errors. New ( "除数不能为零" )
}
return a / b, nil
}
func main ( ) {
result, err := divide ( 10 , 0 )
if err != nil {
fmt. Println ( "发生错误:" , err)
}
}
5.2 错误处理模式
5.2.1 多返回值错误处理
func readFile ( filename string ) ( [ ] byte , error ) {
data, err := ioutil. ReadFile ( filename)
if err != nil {
return nil , fmt. Errorf ( "读取文件失败: %v" , err)
}
return data, nil
}
func processFile ( filename string ) {
data, err := readFile ( filename)
switch {
case err == nil :
fmt. Println ( string ( data) )
case os. IsNotExist ( err) :
fmt. Println ( "文件不存在" )
case os. IsPermission ( err) :
fmt. Println ( "权限不足" )
default :
fmt. Println ( "未知错误:" , err)
}
}
5.2.2 错误类型断言
type ValidationError struct {
Field string
Value interface { }
}
func ( e * ValidationError) Error ( ) string {
return fmt. Sprintf ( "验证错误:字段 %s 值 %v 不合法" , e. Field, e. Value)
}
func validateAge ( age int ) error {
if age < 0 || age > 150 {
return & ValidationError{
Field: "Age" ,
Value: age,
}
}
return nil
}
func main ( ) {
err := validateAge ( - 5 )
if err != nil {
if ve, ok := err. ( * ValidationError) ; ok {
fmt. Printf ( "特定错误处理:%s\n" , ve. Error ( ) )
}
}
}
5.3 panic和recover
5.3.1 panic触发
func riskyOperation ( ) {
panic ( "发生严重错误" )
}
func main ( ) {
riskyOperation ( )
}
5.3.2 recover捕获异常
func recoverExample ( ) {
defer func ( ) {
if r := recover ( ) ; r != nil {
fmt. Println ( "捕获到panic:" , r)
}
} ( )
panic ( "unexpected error" )
}
func main ( ) {
recoverExample ( )
fmt. Println ( "程序继续执行" )
}
5.4 自定义错误处理
5.4.1 错误包装
func wrapError ( err error ) error {
return fmt. Errorf ( "操作失败:%w" , err)
}
func complexOperation ( ) error {
baseErr := errors. New ( "数据库连接失败" )
return wrapError ( baseErr)
}
func main ( ) {
err := complexOperation ( )
if errors. Is ( err, baseErr) {
fmt. Println ( "匹配到原始错误" )
}
}
5.4.2 错误日志记录
type Logger struct {
file * os. File
}
func ( l * Logger) LogError ( err error ) {
if err != nil {
logEntry := fmt. Sprintf ( "%s - Error: %v\n" ,
time. Now ( ) . Format ( time. RFC3339) , err)
l. file. WriteString ( logEntry)
}
}
func main ( ) {
logger, _ := os. OpenFile ( "error.log" , os. O_APPEND| os. O_CREATE| os. O_WRONLY, 0644 )
defer logger. Close ( )
customLogger := & Logger{ file: logger}
err := someRiskyOperation ( )
customLogger. LogError ( err)
}
5.5 最佳实践
5.5.1 错误处理原则
始终检查错误 只处理可以处理的错误 在合适的抽象层级传播错误 避免忽略错误 使用错误包装增加上下文信息
5.5.2 常见错误处理模式
func processData ( data [ ] byte ) error {
if len ( data) == 0 {
return errors. New ( "数据为空" )
}
return nil
}
func main ( ) {
data := [ ] byte { }
if err := processData ( data) ; err != nil {
log. Printf ( "处理失败:%v" , err)
return
}
}
实践项目:简单的配置文件解析器
type Config struct {
Database struct {
Host string
Port int
}
}
func parseConfig ( filename string ) ( * Config, error ) {
data, err := ioutil. ReadFile ( filename)
if err != nil {
return nil , fmt. Errorf ( "读取配置文件失败: %w" , err)
}
var config Config
if err := json. Unmarshal ( data, & config) ; err != nil {
return nil , fmt. Errorf ( "解析配置文件失败: %w" , err)
}
return & config, nil
}
func main ( ) {
config, err := parseConfig ( "config.json" )
if err != nil {
switch {
case os. IsNotExist ( err) :
log. Fatal ( "配置文件不存在" )
case os. IsPermission ( err) :
log. Fatal ( "无权限读取配置文件" )
default :
log. Fatalf ( "配置解析错误: %v" , err)
}
}
fmt. Printf ( "数据库配置:%+v\n" , config. Database)
}