В постах Golang puzzlers мы рассмотрим различные случаи в Golang, которые могут быть неочевидными на первый взгляд.
Пример этого поста. Дан код:
package main
import (
"fmt"
)
type myType struct {
msg string
}
func (m *myType) Error() string {
return m.msg
}
func test() *myType {
return nil
}
func main() {
var err error
err = test()
if err != nil {
fmt.Println("occured error")
return
}
fmt.Println("ok")
}
Вопрос: что выведет данный код?
На первый взгляд все очевидно: задан тип myType, указатель на который реализует интерфейс error. Раз указатель на myType реализует интерфейс ошибки, то кажется что все верно - функция test возвращает указатель на myType, а в функции main возвращаемое значение будет записано в переменную типа error. При запуске программа скомпилируется и кажется, что раз мы возвращаем nil в test, то будет выведено ok, но в консоль выведется occured error!
Начнем разбор - распечатаем переменную err, которая должна быть равна nil:
package main
import (
"fmt"
)
type myType struct {
msg string
}
func (m *myType) Error() string {
return m.msg
}
func test() *myType {
return nil
}
func main() {
var err error
err = test()
if err != nil {
fmt.Println("occured error")
fmt.Println(err)
return
}
fmt.Println("ok")
}
При запуске получим:
occured error
<nil>
Значит err равно nil? В чем подвох? Мы сможем понять это использовав пакет reflect для разбора:
package main
import (
"fmt"
"reflect"
)
type myType struct {
msg string
}
func (m *myType) Error() string {
return m.msg
}
func test() *myType {
return nil
}
func main() {
var err error
err = test()
if err != nil {
fmt.Println("error")
fmt.Println(err)
e := reflect.ValueOf(err)
fmt.Println(e)
fmt.Println(e.Kind())
return
}
fmt.Println("ok")
fmt.Println(err)
}
При запуске получим:
error
<nil>
<nil>
ptr
Что получается? В err записывается указатель со значением nil. Это указатель на тип myType, который реализует интерфейс error, поэтому этот указатель можно записать в переменную типа error - компилятор позволит нам это. Но при проверке на nil за nil засчитается только значение записанное по типу интерфейса error - то есть если бы функция test в качестве возвращаемого значения в сигнатуре функции указывала бы интерфейс error, тогда все бы работало как предполагается и в консоль было бы выведено ok.
package main
import (
"fmt"
)
type myType struct {
msg string
}
func (m *myType) Error() string {
return m.msg
}
func test() error {
return nil
}
func main() {
var err error
err = test()
if err != nil {
fmt.Println("error")
return
}
fmt.Println("ok")
}
При запуске получим:
ok
Запустить пример в песочнице play.golang.org
Читайте также:
Комментариев нет:
Отправить комментарий