На работе завязалась интересная дискуссия: если в Go вообще всё можно привести к интерфейсу, является ли интерфейс некой базой для любого типа в Go? Как отслеживаются ошибки приведения типов для таких случаев? К примеру возьмем следующий код:
"fmt"
"reflect"
)
func interfaceArg(i interface{}) {
fmt.Println("type:", reflect.TypeOf(i))
}
func main() {
var x float64 = 3.14
interfaceArg(x)
}
На выходе получим как и ожидалось:
Если заглянуть в ассемблер, то становится очевидным что приведение к типу “интерфейс” проходит на лету перед вызовом функции
Хорошо, но что с обработкой ошибок преобразования? В документации Go четко сказано, что проверка на корректность приведения интерфейса к типу проводится только в время исполнения. Значит, следующее небольшое изменение в функции
val := i.(int)
fmt.Println(val)
}
Но упадет на во время исполнения, так как в функцию была передана информация о том, что тип у нас все же float64:
Ассемблерное преобразования в
pcdata $0, $0
movq "".i+96(SP), AX
pcdata $2, $1
leaq type.int(SB), CX
cmpq AX, CX
jne interfaceArg_pc164
pcdata $2, $2
pcdata $0, $1
movq "".i+104(SP), AX
pcdata $2, $0
movq (AX), AX
Где
var x float64 = 3.14
var y interface{} = x
z := y.(int)
fmt.Println(z)
}
К ошибке на этапе компиляции этот код, ожидаемо не приведет и мы получим панику во время выполнения. Но что же там в ассемблере?!
pcdata $0, $0
leaq type.float64(SB), AX
pcdata $2, $0
movq AX, (SP)
pcdata $2, $1
leaq type.int(SB), AX
pcdata $2, $0
movq AX, 8(SP)
pcdata $2, $1
leaq type.interface {}(SB), AX
pcdata $2, $0
movq AX, 16(SP)
call runtime.panicdottypeE(SB)
undef
А в ассемблере мы получаем безусловный вызов паники
В итоге все вопросы были сняты, ну а случай с заведомо не возможным преобразованием скорее исключение из правил и в реальном коде увидеть такое довольно затруднительно.