interface{}
- это тип интерфейса, и они "хитрые". Они являются обертками вокруг конкретного значения и конкретного типа, схематически пары (значение, тип).
Таким образом, когда вы передаете конкретное значение функции, которая ожидает значение interface{}
, конкретное значение будет автоматически обернуто в значение interface{}
, неявно. Если вы передадите nil
такой функции, само значение интерфейса будет nil
. Если вы передадите на него указатель nil
, например (*int)(nil)
, значение интерфейса будет не nil
, а значение интерфейса, содержащее "(nil, * int)".
Если вы передадите nil
на reflect.ValueOf()
, это приведет к «нулю» reflect.Value
, который вообще не представляет значения. Если вы передадите это reflect.Append()
, у него не будет информации о типе, он не будет знать, что вы хотите добавить к срезу.
Можно создать значение, представляющее значение интерфейса nil
.
Для этого мы можем начать с дескриптора типа значения указателя на интерфейс (указатели на интерфейс редко имеют смысл, но это один из них). Мы переходим к дескриптору типа указанного типа, который равен interface{}
. Мы получаем нулевое значение этого типа (используя reflect.Zero()
), которое равно nil
(нулевое значение типов интерфейса nil
).
Ноль возвращает значение, представляющее нулевое значение для указанного типа. Результат отличается от нулевого значения структуры Value, которая вообще не представляет значения.
Вот как это выглядит:
typeOfEmptyIface := reflect.TypeOf((*interface{})(nil)).Elem()
valueOfZeroEmptyIface := reflect.Zero(typeOfEmptyIface)
v = reflect.Append(v, valueOfZeroEmptyIface)
или одной строкой:
v = reflect.Append(v, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
Чтобы проверить результаты, давайте использовать:
fmt.Printf("%#v\n", v)
А также давайте введем обратно срез и добавим значение nil
, используя встроенную функцию append()
:
list = v.Interface().([]interface{})
list = append(list, nil)
fmt.Printf("%#v\n", list)
Давайте сделаем явную дополнительную проверку, если элементы nil
(сравните их с nil
). Хотя при использовании глагола %#v
это избыточно, %v
предпочитает печатать не-1054 * интерфейсы, содержащие nil
конкретные значения, так же как nil
(так же, как если бы само значение интерфейса было nil
).
fmt.Println(list[2] == nil, list[3] == nil)
Выход будет (попробуйте на Go Playground ):
[]interface {}{1, "1", interface {}(nil)}
[]interface {}{1, "1", interface {}(nil), interface {}(nil)}
true true
См. Связанный вопрос: Скрытие нулевых значений, понимание того, почему здесь не работает golang
Также: Блог Go: законы отражения