我想了解为啥要创建一种类型来处理 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.Newfmt.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)

键入IntAIntB,满足接口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 中的错误以及您如何决定它应该具有啥基础类型的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 中的子组件有啥用,为啥要创建子组件?

c基础 数组

要创建一开始没有特定维度的类 Vector,请在类中创建一个方法,允许向向量添加维度

使用回调和函数作为go中的类型

为啥 Go 有几种不同的整数类型?

区块链技术语言(二十六)——Go语言异常处理