英文:
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之旅-练习:错误》进行学习。它会指导我在一个求平方根的函数中添加错误处理。
这是我的解决方案:
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"?
-
为什么要创建一个类型?为什么要创建一个错误类型并为其添加一个方法,而不是只有一个独立的函数?
我是 Go 的初学者,希望能够理解。非常感谢。
英文:
I'm working through A Tour of Go - Exercise: Errors. It holds my hand as I add error handling to a square root function.
Here is my solution:
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))
}
I am having trouble understanding the line:
type ErrNegativeSqrt float64
I have two questions:
- Why is the underlying type of ErrNegativeSqrt "float64" and not "error"?
and
- Why create a type in the first place? Why do we create an error type and add a method to it instead of just having a free-standing function?
I'm a beginner at Go and I'd like to understand. Thank you so much.
答案1
得分: 4
我先回答你的第二个问题:你需要为你的类型添加一个方法,这样ErrNegativeSqrt就实现了error接口,这意味着其他不了解ErrNegativeSqrt具体细节的代码可以将其用作任何其他错误一样使用。
由于error接口只要求一个返回类型为string的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)
}
英文:
I'm gonna answer your second question first: you add a method to your type so ErrNegativeSqrt implements the error interface, which means other code that does not know about the specifics of ErrNegativeSqrt can use it as any other error.
As the error interface only requires a method Error() with return type string, a common type to use is just a string with the Error method returning that string (defined in package "errors").
I don't know why it was decided to use float64 as the error type in this case, but the main advantage that I see is that if you had several functions that could return a similar error, you'd have to do the following:
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)
}
And repeat for any function that needs to return a similar thing. With the code that you posted, you only declare the number responsible for the error when you create it, and the method "Error()" returns the formated string for all.
func Method1() error{
...
ErrNegativeSqrt(number)
}
func Method2() error{
...
ErrNegativeSqrt(number)
}
答案2
得分: 3
通常情况下,我们不会创建自定义的错误类型,但这是一个演示错误工作原理以及其他Go概念的示例。
为什么ErrNegativeSqrt的底层类型是"float64"而不是"error"?
这样可以直接将Sqrt
中的float64
值转换为错误。它还演示了新类型可以具有不同的底层类型,并且方法可以附加到除结构体以外的类型上。如果您需要创建自己的错误类型,请使用最方便的底层类型。
为什么首先要创建一个类型?为什么我们要创建一个错误类型并向其添加一个方法,而不是只有一个独立的函数?
有时,错误需要比errors.New
或fmt.Errorf
返回的单个字符串中包含的信息或上下文更多。例如,net
包使用net.OpError
(符合net.Error
接口)来封装有关地址的信息,以及它是临时错误还是由超时引起的错误。
在大多数情况下,简单的基于字符串的错误就足够了。
英文:
One usually doesn't create custom error types, but this is a demonstration of how errors work, as well as some other Go concepts.
> Why is the underlying type of ErrNegativeSqrt "float64" and not "error"?
This allows the float64
value in Sqrt
to be directly converted to an error. It also demonstrates how new types can have various underlying types, and methods can be attached to types other than structs. If you need to create your own type of error, use the underlying type that is most convenient for your use case.
> Why create a type in the first place? Why do we create an error type and add a method to it instead of just having a free-standing function?
Sometimes an error needs to have more information or context than can be contained in the single string returned from errors.New
, or fmt.Errorf
. For example, the net
package uses a net.OpError
(which conforms to the net.Error
interface) to package information about addresses, whether it's a temporary error, or a caused by a timeout.
In most cases a simple string-based error will suffice.
答案3
得分: 1
和其他语言一样,我们创建类型或对象是为了使其有意义并将功能分组,以便我们自己使用(计算机并不关心)。在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)
了解了接口的概念后,现在回到ErrNegativeSqrt
类型,通过为其添加Error()
方法,该类型实现了error
接口(该接口只有一个Error()
方法需要实现)。现在,您可以在任何地方将该类型用作错误。
这就是Golang中的鸭子类型。
英文:
Like any other languages, we create a type or object to make sense and group functionalities for our own sakes (computer doesn't care). In Go, it is common to create a custom type or a struct type to extend the functionalities of an object.
In this case, creating a type ErrNegativeSqrt
you can actually extend a float64
functionalities by adding methods. For instance, compare this two approaches:
func TimesTwo(val int) int {
return val * 2
}
with
type SuperChargedInt int
func (sc SuperChargedInt) TimesTwo() int {
return int(sc) * 2
}
Instead of passing an int
to a function, a SuperChargedInt
has the capability to act on itself. This becomes very handy when you tread into interfaces.
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)
}
Type IntA
and IntB
, satisfying the interface MyInt
because they both implement the required methods, can be use anywhere in the code that requires MyInt
.
var a IntA = 34
var b IntB = 32
PrintMyInt(a)
PrintMyInt(b)
type SomeNum struct {
A MyInt
}
num := SomeNum{ A: a }
With the understanding of interfaces, now coming back to ErrNegativeSqrt
type, adding Error()
method to it make the type implements the error
interface (which has only one method Error()
to implement). Now you can use the type just like an error anywhere.
This is the duck-typing in Golang.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论