Description
Proposal Details
The current errors.As
usage is non-sensical. In so much as we have to use two different functions already, the fact the following code will compile and fail despite looking right is madness:
package main
import (
"errors"
"fmt"
)
type ErrNew struct {
}
func (e ErrNew) Error() string {
return "I'm an error"
}
func fn() error {
return &ErrNew{}
}
func main() {
if err := fn(); errors.As(err, &ErrNew{}) {
fmt.Println("Got the error")
} else {
fmt.Println("Missed it")
}
}
The correct code is of course this:
package main
import (
"errors"
"fmt"
)
type ErrNew struct {
}
func (e ErrNew) Error() string {
return "I'm an error"
}
func fn() error {
return &ErrNew{}
}
func main() {
x := &ErrNew{}
if err := fn(); errors.As(err, &x) {
fmt.Println("Got the error")
} else {
fmt.Println("Missed it")
}
}
But...why? errors
is already using reflection to identify types here, and when we evaluate the first from we correctly deduce that err
is *ErrNew
- the problem is we convert targetType
into ErrNew
and then check for assignability....which obviously doesn't work because a pointer can't be assigned to a concrete struct which is the type we're comparing to now.
Basically why is As
setup such that this very obvious way of using it doesn't work:
err := &ErrorNew{}
errors.As(err, &ErrNew{})
EDIT: I acknowledge there might be very good reasons this was done this way, but from an ergonomics perspective it's a disaster that Go will happily compile this and it will not work at all how you would expect - plus it's a lot of noise for those wanting to use typed errors.