我想了解为啥要创建一种类型来处理 Go 中的错误以及您如何决定它应该具有啥基础类型
Posted
技术标签:
【中文标题】我想了解为啥要创建一种类型来处理 Go 中的错误以及您如何决定它应该具有啥基础类型【英文标题】:I'd like to understand why one would create a type for handling errors in Go and how you decide what underlying type it should have我想了解为什么要创建一种类型来处理 Go 中的错误以及您如何决定它应该具有什么基础类型 【发布时间】:2016-06-30 02:40:12 【问题描述】:我正在处理A Tour of Go - Exercise: Errors。当我向平方根函数添加错误处理时,它握着我的手。
这是我的解决方案:
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string
fmt.Sprint(float64(e))
return fmt.Sprintf("cannot Sqrt negative number: %g", float64(e))
func Sqrt(x float64) (float64, error)
z := 1.0
margin := 0.00000000000001
for
if x < 0
return 0, ErrNegativeSqrt(x)
previousZ := z
z = z - (z*z-x)/(2*z)
if math.Abs(previousZ-z) < margin
fmt.Println(previousZ, "-", z, "=", previousZ-z)
break
fmt.Println("math.Sqrt:", math.Sqrt(x))
return z, nil
func main()
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
我无法理解这条线:
type ErrNegativeSqrt float64
我有两个问题:
-
为什么 ErrNegativeSqrt 的底层类型是“float64”而不是“error”?
和
-
为什么首先要创建一个类型?为什么我们要创建一个错误类型并为其添加一个方法,而不是仅仅拥有一个独立的函数?
我是围棋的初学者,我想了解一下。非常感谢。
【问题讨论】:
【参考方案1】:我要先回答你的第二个问题:你在你的类型中添加了一个方法,所以 ErrNegativeSqrt 实现了error interface,这意味着其他不知道 ErrNegativeSqrt 细节的代码可以将它用作任何其他错误。
由于错误接口只需要一个返回类型为字符串的方法Error(),一个常用的类型就是一个带有返回该字符串的Error方法的字符串(在包“errors”中定义)。
我不知道为什么在这种情况下决定使用 float64 作为错误类型,但我看到的主要优点是,如果您有多个可能返回类似错误的函数,则必须这样做以下:
func Method1() error
...
return errors.New(fmt.Sprintf("cannot Sqrt negative number: %g", number)
func Method2() error
...
return errors.New(fmt.Sprintf("cannot Sqrt negative number: %g", number2)
对任何需要返回类似内容的函数重复此操作。使用您发布的代码,您只需在创建错误时声明导致错误的数字,并且方法“Error()”返回所有格式化的字符串。
func Method1() error
...
ErrNegativeSqrt(number)
func Method2() error
...
ErrNegativeSqrt(number)
【讨论】:
【参考方案2】:通常不会创建自定义错误类型,但这是对错误如何工作以及其他一些 Go 概念的演示。
为什么 ErrNegativeSqrt 的底层类型是“float64”而不是“error”?
这允许将Sqrt
中的float64
值直接转换为错误。它还演示了新类型如何具有各种基础类型,以及方法可以附加到结构以外的类型。如果您需要创建自己的错误类型,请使用对您的用例最方便的基础类型。
为什么首先要创建一个类型?为什么我们要创建一个错误类型并为其添加一个方法,而不是仅仅拥有一个独立的函数?
有时一个错误需要比errors.New
或fmt.Errorf
返回的单个字符串中包含的更多信息或上下文。例如net
包使用net.OpError
(符合net.Error
接口)来打包地址信息,无论是临时错误,还是超时造成的。
在大多数情况下,一个简单的基于字符串的错误就足够了。
【讨论】:
【参考方案3】:与任何其他语言一样,我们创建一个类型或对象以使其有意义并为我们自己的利益分组功能(计算机不在乎)。在 Go 中,创建自定义类型或结构类型来扩展对象的功能是很常见的。
在这种情况下,创建ErrNegativeSqrt
类型实际上可以通过添加方法来扩展float64
功能。例如,比较这两种方法:
func TimesTwo(val int) int
return val * 2
与
type SuperChargedInt int
func (sc SuperChargedInt) TimesTwo() int
return int(sc) * 2
不是将int
传递给函数,而是SuperChargedInt
具有作用于自身的能力。当您进入界面时,这会变得非常方便。
type MyInt interface
TimesTwo() int
type IntA int
type IntB int
func (a IntA) TimesTwo() int
return int(a) * 2
func (b IntB) TimesTwo() int
return int(b) * (1 + 1)
func PrintMyInt(c MyInt)
fmt.Println(c)
键入IntA
和IntB
,满足接口MyInt
,因为它们都实现了所需的方法,可以在需要MyInt
的代码中的任何地方使用。
var a IntA = 34
var b IntB = 32
PrintMyInt(a)
PrintMyInt(b)
type SomeNum struct
A MyInt
num := SomeNum A: a
了解了接口,现在回到ErrNegativeSqrt
类型,给它添加Error()
方法,使得该类型实现了error
接口(只有一个方法Error()
可以实现)。现在你可以在任何地方像错误一样使用类型了。
这是 Golang 中的鸭式打字。
【讨论】:
以上是关于我想了解为啥要创建一种类型来处理 Go 中的错误以及您如何决定它应该具有啥基础类型的主要内容,如果未能解决你的问题,请参考以下文章