#go #pointers
#Вперед #указатели
Вопрос:
Я заметил *(*int)(nil) = 0
, что в функции есть одна строка throw
//go:nosplit
func throw(s string) {
// Everything throw does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
systemstack(func() {
print("fatal error: ", s, "n")
})
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
fatalthrow()
*(*int)(nil) = 0 // not reached
}
Что *(*int)(nil) = 0
означает? и поскольку эта строка *(*int)(nil) = 0
НЕ может быть достигнута, почему она здесь? какое-либо специальное использование?
Комментарии:
1. Это внутренний вариант компилятора
panic("unreachable")
.
Ответ №1:
Строка:
*(*int)(nil) = 0
Пытается разыменовать nil
указатель и присвоить ему значение, что всегда вызывает панику во время выполнения. Код никогда не должен доходить до этой строки, но если это произойдет в любом случае (например, ошибочное изменение кода в будущем), это вызовет панику, поэтому ошибка может быть обнаружена и не останется незамеченной.
В вашем коде тоже есть здравый смысл сделать что-то подобное, но с более очевидной «конструкцией», такой как panic("unreachable")
. Например:
func sign(a int) string {
switch {
case a > 0:
return "Positive"
case a < 0:
return "Negative"
case a == 0:
return "Zero"
default:
panic("unreachable")
}
}
Обратите внимание, что в этом примере это делается не только для раннего обнаружения ошибок, но и для того, чтобы компилятор не мог гарантировать, что оператор return будет достигнут. Вы также можете переместить panic("unreachable")
оператор после switch
(вместо default
ветки), это дело вкуса.
Если вы измените приведенную выше функцию, чтобы не возвращать, а печатать знак, все равно было бы хорошей практикой оставлять default
ветку для паники, хотя в этом варианте это не обязательно:
func printSign(a int) {
switch {
case a > 0:
fmt.Println("Positive")
case a < 0:
fmt.Println("Negative")
case a == 0:
fmt.Println("Zero")
default:
panic("unreachable")
}
}