3 ms·
Generally, returning a typed error in the function signature in Go is very annoying and dangerous. An example why: import "fmt" type MyError struc
by im-a-baby 4y ago
Generally, returning a typed error in the function signature in Go is very annoying and dangerous. An example why:
import "fmt"
type MyError struct{}
func (m *MyError) Error() string {
return ""
}
func myFunc() *MyError {
return nil
}
func main() {
var err error
err = myFunc()
if err == nil {
fmt.Println("No Error")
} else {
fmt.Println("Error") // This prints
}
}
- svnpenn 4y agothats kind of misleading, as you could just do: func myFunc() error
- im-a-baby 4y agoYes you could do that, but then you’d lose type info on the error.
- gregwebs 4y agoI agree this is awful, but isn't it solved here by not using pointers? I don't remember coming across a case where a custom error type benefited from being a pointer. I am sure such a case exists, but then such a case should be used when giving example code.
- im-a-baby 4y agoNo not using pointers would not solve the issue. The issue is that the specific error implementation type is part of the method signature. If you returned type MyError instead of *MyError, then it would be impossible to return nil from the method because only pointers can be nil.
- gregwebs 4y agoI get that. But why define the Error method on a pointed receiver? In my experience almost all errors can be immutable particularly once you start using wrapping.
- gregwebs 4y agoNever mind- that doesn't actually make a difference. The solution in this case is to not re-use an existing error variable- but re-use of an error variable is common practice.
- im-a-baby 4y agoYea, the issue is that nil's in Go are typed. So `err == nil` is checking err against error's nil value, but the value stored in err is *MyError's nil value. package main import "fmt" type MyError struct{} func (m *MyError) Error() string { return "" } func main() { var err error = nil var myErr *MyError = nil fmt.Println(err == myErr) // this is false }
- gregwebs 4y agoComparing different types normally is a type error. But for interfaces the non-interface value will be converted to the type of the interface value. This conversion gives it a boxed dynamic type. This is different type than just assigning nil, which is the untyped nil, which sets the interface to its zero value. Normally one would not compare two interface values, so this issue really comes up when comparing an interface to nil. If Go had proper option types then this issue would go away. https://go101.org/article/nil.html https://go101.org/article/nil.html package main import "fmt" type Err1 struct{} func (m Err1) Error() string { return "" } type Err2 struct{} func (m Err2) Error() string { return "" } func main() { var err error = nil var myErr1 *Err1 = nil var myErr2 *Err2 = nil fmt.Println(err == myErr1) // this is false fmt.Println(err == myErr2) // this is false fmt.Println((error)(myErr1) == (error)(myErr2)) // this is false. requires the cast to do the comparison }