您的位置: > 工作总结 >

100天精通Golang(基础入门篇)

时间:2023-09-30 13:45:42  来源:养殖之家网   作者:随风

100天精通Golang(基础入门篇)

🌷🍁 博主猫头虎🐅🐾 带您进入 Golang 语言的新世界✨✨🍁
🦄 博客首页——🐅🐾猫头虎的博客🎐
🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐
🌊 《100天精通Golang(基础入门篇)》 🐅 学会Golang语言,畅游云原生领域,无厂不可去~💐

🪁🍁 希望本文能给您带来价值🌸如果有任何不足,欢迎批评指正!🐅🐾🍁🐥

 

100天精通Golang(基础入门篇)

100天精通Golang(基础入门篇)第23天:Go异常错误处理解决方案 🐾

引言 📘

1.1 错误的定义

错误与异常的区别

Go中的错误处理

错误与异常的区别

Go中的错误处理

1.2 演示错误

1.3 错误类型表示

1.4 自定义错误

1.5 panic()和recover()

1.6 错误处理的正确姿势

姿势案例一:失败的原因只有一个时,不使用error**

姿势案例二:没有失败时,不使用error**

姿势案例三:error应放在返回值类型列表的最后**

姿势案例四:错误值统一定义,而不是跟着感觉走**

姿势案例五:错误逐层传递时,层层都加日志**

姿势案例六:错误处理使用defer**

姿势案例七:当尝试几次可以避免失败时,不要立即返回错误**

姿势案例八:当上层函数不关心错误时,建议不返回error**

姿势案例九:当发生错误时,不忽略有用的返回值**

1.7 异常处理的正确姿势

姿势案例一:在程序开发阶段,坚持速错**

姿势案例二:在程序部署后,应恢复异常避免程序终止**

姿势案例三:对于不应该出现的分支,使用异常处理**

姿势案例四:针对入参不应该有问题的函数,使用panic设计**

扩展知识点 🎓

Panic

Recover

今日学习总结 🎉

结语

 

100天精通Golang(基础入门篇)第23天:Go异常错误处理解决方案 🐾

大家好,我是猫头虎!今天我们继续探索Go语言的奥秘,迎来了我们的第23天学习之旅。在这一天,我们将重点关注Go语言中的错误处理机制。在实际的工程项目中,通过程序错误信息快速定位问题是我们的期望,但我们又不希望错误处理代码显得冗余和啰嗦。Go语言通过函数返回值逐层向上抛出错误,与Java和C#的try...catch异常处理显著不同。这种设计理念鼓励工程师显式地检查错误,以避免忽略应处理的错误,从而确保代码的健壮性。🚀

引言 📘

在Go语言中,错误被视为一种数据类型,使用内置的 error 类型来表示。区分 错误异常 是非常重要的。错误通常指的是预期可能会出现问题的地方确实出现了问题,而异常则是指在不应出现问题的地方却出现了问题。这种区分帮助我们更好地理解错误处理的重要性,并在实际编程中做出正确的决策。

在实际的工程项目中,快速通过程序错误信息定位问题是我们的期望,但同时我们又不希望错误处理代码显得冗余和啰嗦。与 Java 和 C# 的 try...catch 异常处理不同,Go 语言选择通过函数返回值逐层向上抛出错误。这种设计理念鼓励工程师显式地检查错误,以避免忽略应处理的错误,从而确保代码的健壮性。然而,这也导致了代码可能会显得较为啰嗦。

1.1 错误的定义

错误与异常的区别

错误通常指的是预期可能会出现问题的地方确实出现了问题。例如,当尝试打开一个文件失败时,这种情况通常是可以预料到的。

异常则是指在不应出现问题的地方却出现了问题。例如,发生了空指针引用,这种情况通常是出乎人们的意料之外的。

从这可以看出,错误通常是业务过程的一部分,而异常则不是。

Go中的错误处理

在 Go 语言中,错误被视为一种数据类型,使用内置的 error 类型来表示。就像其他基本类型(如 int, float64)一样,错误值可以被存储在变量中,或者从函数中返回等等。## 1.1 错误的定义

错误与异常的区别

错误通常指的是预期可能会出现问题的地方确实出现了问题。例如,当尝试打开一个文件失败时,这种情况通常是可以预料到的。

异常则是指在不应出现问题的地方却出现了问题。例如,发生了空指针引用,这种情况通常是出乎人们的意料之外的。

从这可以看出,错误通常是业务过程的一部分,而异常则不是。

Go中的错误处理

在 Go 语言中,错误被视为一种数据类型,使用内置的 error 类型来表示。就像其他基本类型(如 int, float64)一样,错误值可以被存储在变量中,或者从函数中返回等等。

1.2 演示错误

让我们从一个示例程序开始,这个程序尝试打开一个不存在的文件。

示例代码:

package mainimport ( "fmt""os" )func main() {f, err := os.Open("/test.txt")if err != nil {fmt.Println(err)return}//根据f进行文件的读或写fmt.Println(f.Name(), "opened successfully") }

在os包中有打开文件的功能函数:

​ func Open(name string) (file *File, err error)

如果文件已经成功打开,那么Open函数将返回文件处理。如果在打开文件时出现错误,将返回一个非nil错误。

如果一个函数或方法返回一个错误,那么按照惯例,它必须是函数返回的最后一个值。因此,Open 函数返回的值是最后一个值。

处理错误的惯用方法是将返回的错误与nil进行比较。nil值表示没有发生错误,而非nil值表示出现错误。在我们的例子中,我们检查错误是否为nil。如果它不是nil,我们只需打印错误并从主函数返回。

运行结果:

open /test.txt: No such file or directory

我们得到一个错误,说明该文件不存在。

1.3 错误类型表示

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

让我们再深入一点,看看如何定义错误类型的构建。错误是一个带有以下定义的接口类型,

type error interface {Error() string }

它包含一个带有Error()字符串的方法。任何实现这个接口的类型都可以作为一个错误使用。这个方法提供了对错误的描述。

当打印错误时,fmt.Println函数在内部调用Error() 方法来获取错误的描述。这就是错误描述是如何在一行中打印出来的。

从错误中提取更多信息的不同方法

既然我们知道错误是一种接口类型,那么让我们看看如何提取更多关于错误的信息。

在上面的例子中,我们仅仅是打印了错误的描述。如果我们想要的是导致错误的文件的实际路径。一种可能的方法是解析错误字符串。这是我们程序的输出,

open /test.txt: No such file or directory

我们可以解析这个错误消息并从中获取文件路径"/test.txt"。但这是一个糟糕的方法。在新版本的语言中,错误描述可以随时更改,我们的代码将会中断。

是否有办法可靠地获取文件名?答案是肯定的,它可以做到,标准Go库使用不同的方式提供更多关于错误的信息。让我们一看一看。

1.断言底层结构类型并从结构字段获取更多信息

如果仔细阅读打开函数的文档,可以看到它返回的是PathError类型的错误。PathError是一个struct类型,它在标准库中的实现如下,

type PathError struct {Op stringPath stringErr error }func (e *PathError) Error() string {return e.Op + " " + e.Path + ": " + e.Err.Error() }

从上面的代码中,您可以理解PathError通过声明Error()string方法实现了错误接口。该方法连接操作、路径和实际错误并返回它。这样我们就得到了错误信息,

open /test.txt: No such file or directory

PathError结构的路径字段包含导致错误的文件的路径。让我们修改上面写的程序,并打印出路径。

修改代码:

package mainimport ( "fmt""os" )func main() {f, err := os.Open("/test.txt")if err, ok := err.(*os.PathError); ok {fmt.Println("File at path", err.Path, "failed to open")return}fmt.Println(f.Name(), "opened successfully") }

在上面的程序中,我们使用类型断言获得错误接口的基本值。然后我们用错误来打印路径.这个程序输出,

File at path /test.txt failed to open

断言底层结构类型,并使用方法获取更多信息

获得更多信息的第二种方法是断言底层类型,并通过调用struct类型的方法获取更多信息。

示例代码:

type DNSError struct {... }func (e *DNSError) Error() string {... } func (e *DNSError) Timeout() bool {... } func (e *DNSError) Temporary() bool {... }

从上面的代码中可以看到,DNSError struct有两个方法Timeout() bool和Temporary() bool,它们返回一个布尔值,表示错误是由于超时还是临时的。

让我们编写一个断言*DNSError类型的程序,并调用这些方法来确定错误是临时的还是超时的。

package mainimport ( "fmt""net" )func main() {addr, err := net.LookupHost("golangbot123.com")if err, ok := err.(*net.DNSError); ok {if err.Timeout() {fmt.Println("operation timed out")} else if err.Temporary() {fmt.Println("temporary error")} else {fmt.Println("generic error: ", err)}return}fmt.Println(addr) }

在上面的程序中,我们正在尝试获取一个无效域名的ip地址,这是一个无效的域名。golangbot123.com。我们通过声明它来输入*net.DNSError来获得错误的潜在价值。

在我们的例子中,错误既不是暂时的,也不是由于超时,因此程序会打印出来,

generic error: lookup golangbot123.com: no such host

如果错误是临时的或超时的,那么相应的If语句就会执行,我们可以适当地处理它。

3.直接比较

获得更多关于错误的详细信息的第三种方法是直接与类型错误的变量进行比较。让我们通过一个例子来理解这个问题。

filepath包的Glob函数用于返回与模式匹配的所有文件的名称。当模式出现错误时,该函数将返回一个错误ErrBadPattern。

在filepath包中定义了ErrBadPattern,如下所述:

var ErrBadPattern = errors.New("syntax error in pattern")

errors.New()用于创建新的错误。

当模式出现错误时,由Glob函数返回ErrBadPattern。

让我们写一个小程序来检查这个错误:

package mainimport ( "fmt""path/filepath" )func main() {files, error := filepath.Glob("[")if error != nil && error == filepath.ErrBadPattern {fmt.Println(error)return}fmt.Println("matched files", files) }

运行结果:

syntax error in pattern

不要忽略错误

永远不要忽略一个错误。忽视错误会招致麻烦。让我重新编写一个示例,该示例列出了与模式匹配的所有文件的名称,而忽略了错误处理代码。

package mainimport ( "fmt""path/filepath" )func main() {files, _ := filepath.Glob("[")fmt.Println("matched files", files) }

我们从前面的例子中已经知道模式是无效的。我忽略了Glob函数返回的错误,方法是使用行号中的空白标识符。

matched files []

由于我们忽略了这个错误,输出看起来好像没有文件匹配这个模式,但是实际上这个模式本身是畸形的。所以不要忽略错误。

1.4 自定义错误

创建自定义错误可以使用errors包下的New()函数,以及fmt包下的:Errorf()函数。

//errors包: func New(text string) error {}//fmt包: func Errorf(format string, a ...interface{}) error {}

在使用New()函数创建自定义错误之前,让我们了解它是如何实现的。下面提供了错误包中的新功能的实现。

// Package errors implements functions to manipulate errors.package errors// New returns an error that formats as the given text.func New(text string) error {return &errorString{text}}// errorString is a trivial implementation of error.type errorString struct {s string}func (e *errorString) Error() string {return e.s}

既然我们知道了New()函数是如何工作的,那么就让我们在自己的程序中使用它来创建一个自定义错误。

我们将创建一个简单的程序,计算一个圆的面积,如果半径为负,将返回一个错误。

package mainimport ( "errors""fmt""math" )func circleArea(radius float64) (float64, error) {if radius < 0 {return 0, errors.New("Area calculation failed, radius is less than zero")}return math.Pi * radius * radius, nil }func main() {radius := -20.0area, err := circleArea(radius)if err != nil {fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area) }

运行结果:

Area calculation failed, radius is less than zero

使用Errorf向错误添加更多信息

上面的程序运行得很好,但是如果我们打印出导致错误的实际半径,那就不好了。这就是fmt包的Errorf函数的用武之地。这个函数根据一个格式说明器格式化错误,并返回一个字符串作为值来满足错误。

使用Errorf函数,修改程序。

package mainimport ( "fmt""math" )func circleArea(radius float64) (float64, error) {if radius < 0 {return 0, fmt.Errorf("Area calculation failed, radius %0.2f is less than zero", radius)}return math.Pi * radius * radius, nil }func main() {radius := -20.0area, err := circleArea(radius)if err != nil {fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area) }

运行结果:

Area calculation failed, radius -20.00 is less than zero

使用struct类型和字段提供关于错误的更多信息

还可以使用将错误接口实现为错误的struct类型。这给我们提供了更多的错误处理的灵活性。在我们的示例中,如果我们想要访问导致错误的半径,那么现在唯一的方法是解析错误描述区域计算失败,半径-20.00小于零。这不是一种正确的方法,因为如果描述发生了变化,我们的代码就会中断。

我们将使用在前面的教程中解释的标准库的策略,在“断言底层结构类型并从struct字段获取更多信息”,并使用struct字段来提供对导致错误的半径的访问。我们将创建一个实现错误接口的struct类型,并使用它的字段来提供关于错误的更多信息。

第一步是创建一个struct类型来表示错误。错误类型的命名约定是,名称应该以文本Error结束。让我们把struct类型命名为areaError

type areaError struct {err stringradius float64 }

上面的struct类型有一个字段半径,它存储了为错误负责的半径的值,并且错误字段存储了实际的错误消息。

下一步,是实现error 接口

func (e

上一篇:"数据中心不偏科能“” 下一篇:没有了

相关推荐

养殖之家网速递