G错误处理.go

前言:错误处理是构建稳健、可靠应用的关键环节。 

目录

一、error:Go 语言里的 “报错小信使”

二、多值返回与错误检查:Go 的 “双保险” 机制

三、自定义错误类型:给错误 “量身定做” 小马甲

四、包装错误:给错误添点 “背景故事”

五、代码示例:综合运用错误处理技巧,搞定文件读取

总结

当好程序里的 “靠谱王”


一、error:Go 语言里的 “报错小信使”

Go 语言里,错误是通过 error 接口来表示的,它就像是一个专门传递 “出事了” 信号的小信使。当函数运行不顺,比如找不着文件、网络连不上等情况时,就会返回一个 error 值。

来看看这个打开文件的简单例子:

package main

import (
	"fmt"
	"os"
)

func main() {
	// 嘿,我要去打开一个文件咯,希望它别和我闹脾气
	file, err := os.Open("不存在的文件.txt")
	if err != nil {
		// 哎呀妈呀,出错啦!赶紧看看咋回事
		fmt.Println("糟糕,出错啦:", err)
		return
	}
	// 哈哈,文件顺利打开,别忘了后面得关上哦
	defer file.Close()
	fmt.Println("文件乖乖打开了呢")
}

在上面这段代码里,os.Open() 函数就像是个小心翼翼的 “开箱手”,它尝试着去打开文件。要是文件不存在呢,它就返回一个非 nil 的 error,就好像在举着小旗子喊 “这里有情况!”。我们在后面紧接着检查 err,要是它不是 nil,就意味着操作没成功,我们就可以按计划去处理错误啦。

二、多值返回与错误检查:Go 的 “双保险” 机制

Go 语言里好多函数都超级贴心,采用多值返回的方式,最后一个值常常是 error。这就好比是给你上了 “双保险”,让你能同时拿到操作结果和错误信息。

咱再瞅瞅这个计算除法的函数:

package main

import (
	"fmt"
)

func divide(a, b float64) (float64, error) {
	if b == 0 {
		// 嘿,除数不能为零哦,这是数学大忌!
		return 0, fmt.Errorf("拜托,除数不能是零啊")
	}
	return a / b, nil
}

func main() {
	result, err := divide(10, 0)
	if err != nil {
		// 赶紧抓住这个错误,别让它跑啦
		fmt.Println("哎呀,出错咯:", err)
		return
	}
	fmt.Println("结果是:", result)
}

这里 divide 函数在除数为零的时候,就 “温柔” 地返回一个非 nil 的 error,提醒调用者 “这个忙我帮不了,你得换种办法”。调用端拿到这个 error 后,赶紧检查,要是发现问题,就按计划去处理,要是在其他情况呢,就开开心心地用结果。

三、自定义错误类型:给错误 “量身定做” 小马甲

有时候,Go 语言自带的 error 类型就像是一件 “大众款” 外套,不够个性。于是,咱可以给错误 “量身定做” 小马甲,也就是自定义错误类型。

瞧瞧这个自定义错误的小例子: 

package main

import (
	"fmt"
	"os"
)

// 我要给我的错误弄个超酷的 “身份证”
type FileError struct {
	FileName string
	Err      error
}

func (e *FileError) Error() string {
	return fmt.Sprintf("嘿,处理文件 %s 时出问题啦: %s", e.FileName, e.Err)
}

func openFile(fileName string) (*os.File, error) {
	file, err := os.Open(fileName)
	if err != nil {
		// 把错误装扮成我自定义的样子
		return nil, &FileError{FileName: fileName, Err: err}
	}
	return file, nil
}

func main() {
	file, err := openFile("不存在的文件.txt")
	if err != nil {
		// 咦,看看是不是我认识的错误类型
		if fileErr, ok := err.(*FileError); ok {
			fmt.Println("文件错误发生咯:", fileErr.FileName, "-", fileErr.Err)
		} else {
			fmt.Println("出错咯:", err)
		}
		return
	}
	defer file.Close()
	fmt.Println("文件老老实实打开咯")
}

自定义错误类型就像是给错误贴了个独特的标签,当项目变得超大、模块堆成山的时候,靠着这个标签,咱就能快速地找到错误属于哪个模块、哪个环节,处理起来那叫一个得心应手。

四、包装错误:给错误添点 “背景故事”

在复杂的程序里,错误可能会从好多层往下传。这时候,包装错误就像是给错误添点 “背景故事”,让咱在处理错误的时候能知道它都经历了啥。

看看这个包装错误的代码:

package main

import (
	"errors"
	"fmt"
)

func topLevelFunction() error {
	err := midLevelFunction()
	if err != nil {
		// 把底层错误包装一下,加上这层的 “小心情”
		return fmt.Errorf("顶层函数: %w", err)
	}
	return nil
}

func midLevelFunction() error {
	err := lowLevelFunction()
	if err != nil {
		// 中间层也来添点 “小心思”
		return fmt.Errorf("中间层函数: %w", err)
	}
	return nil
}

func lowLevelFunction() error {
	return errors.New("底层函数:我这儿有点小状况")
}

func main() {
	err := topLevelFunction()
	if err != nil {
		fmt.Println("出错咯:", err)
		// 顺藤摸瓜找底层错误
		if errors.Is(err, errors.New("底层函数:我这儿有点小状况")) {
			fmt.Println("最底层的错误被我揪出来咯")
		}
	}
}

在复杂的调用链里,有了包装错误这个 “线索”,咱就能顺着这个链,一层一层地找到问题的根源,然后再针对性地处理,再也不怕在复杂的代码迷宫里迷路咯。

五、代码示例:综合运用错误处理技巧,搞定文件读取

接下来,咱看一个综合运用多种错误处理技巧的代码,看看怎么把文件读取这个事儿做得既稳妥又靠谱:

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

// 嘿,我来定义个自定义错误类型,逼格一下
type FileReadError struct {
	FileName string
	Err      error
}

func (e *FileReadError) Error() string {
	return fmt.Sprintf("读文件 %s 时心情不爽啦: %s", e.FileName, e.Err)
}

func readFile(filePath string) (string, error) {
	// 先打开文件,看看它给不给面子
	file, err := os.Open(filePath)
	if err != nil {
		// 呜哇,打不开文件,包装一下错误再走
		return "", fmt.Errorf("文件打开失败: %w", err)
	}
	defer file.Close() // 记得关文件哦,不然会漏电的

	// 好咯,读文件内容咯
	data, err := ioutil.ReadAll(file)
	if err != nil {
		// 哎呀,读取失败,也包装一下
		return "", fmt.Errorf("文件读取失败: %w", err)
	}

	// 嘿,内容读到咯,开开心心返回
	return string(data), nil
}

func main() {
	filePath := "不存在的文件.txt" // 哼,我故意写个不存在的文件
	content, err := readFile(filePath)
	if err != nil {
		// 哦吼,出错咯,看看是不是我认识的错误类型
		if fileErr, ok := err.(*FileReadError); ok {
			fmt.Println("文件读取错误:", fileErr.FileName, "-", fileErr.Err)
		} else {
			// 哎呀,这错误不认识,也得处理呀
			fmt.Println("出错咯:", err)
		}
		return
	}
	fmt.Println("文件内容:", content)
}

在这个代码里,咱先是定义了个自定义错误类型 FileReadError,然后在 readFile 函数里,先是尝试打开文件,要是失败呢,就包装个错误往回返。要是文件打开了,就接着读内容,要是读的时候出问题,同样包装个错误。调用端拿到这个 error 后,先看看是不是咱定义的自定义错误类型,要是不是,也得好好处理,不能放任不管。

总结

当好程序里的 “靠谱王”

掌握好 Go 语言的错误处理,你就能在程序里当个 “靠谱王” 啦。它能让你的代码面对各种突发情况都能稳稳当当的,不会轻易崩溃,也不会给用户呈现一堆乱七八糟的错误信息。

总之呢,错误处理就像是给你的程序穿上一件 “防弹衣”,让它在复杂多变的运行环境中,依然能够健健康康地工作。要是你想在编程这条路上走得远、走得好,那可千万不能忽视错误处理这个关键技能呀。

你可能感兴趣的:(26字母学习:Go入门篇,golang,开发语言,后端,青少年编程,学习方法,visual,studio,code)