не может понять нулевое значение интерфейса [duplicate]

Вам необходимо URL-кодировать @ как% 40.

30
задан icza 16 February 2016 в 23:50
поделиться

2 ответа

Проблема здесь в том, что shower - это тип interface. Типы интерфейсов в Go удерживают фактическое значение и его динамический тип. Подробнее об этом: Законы отражения # Представление интерфейса .

Выбранный фрагмент содержит 2 значения не nil. Второе значение представляет собой значение интерфейса, пару (значение; тип), содержащее значение nil и тип *display. Цитирование из спецификации языка g1] Go: Операторы сравнения :

Значения интерфейса сопоставимы. Два значения интерфейса равны, если они имеют одинаковые динамические типы и равные динамические значения или оба имеют значение nil.

Поэтому, если вы сравните его с nil, это будет false. Если вы сравните его со значением интерфейса, представляющим пару (nil;*display), это будет true:

if x == (*display)(nil) {
    panic("everything ok, nil found")
}

Это кажется неосуществимым, так как вам нужно знать, какой тип интерфейса имеет.

Почему это реализовано таким образом?

Интерфейсы, в отличие от других конкретных типов (не-интерфейсы), могут содержать значения разных типов конкретных типов (разные статические типы). Время выполнения должно знать динамический или runtime-тип значения, хранящегося в переменной типа интерфейса.

interface - это всего лишь набор методов, любой тип реализует его если одни и те же методы являются частью метода , заданного типа. Существуют типы, которые не могут быть nil, например struct или пользовательский тип с int в качестве базового типа. В этих случаях вам не нужно будет сохранять значение nil этого конкретного типа.

Но любой тип также включает конкретные типы, где nil является действительным (например, фрагменты, карты, каналы, все типы указателей), поэтому, чтобы сохранить значение во время выполнения, которое удовлетворяет интерфейсу, разумно поддерживать сохранение nil внутри интерфейса. Но помимо nil внутри интерфейса мы должны хранить его динамический тип, поскольку значение nil не несет такую ​​информацию. Альтернативным вариантом будет использование nil в качестве самого значения интерфейса, когда значение, которое будет храниться в нем, nil, но это решение недостаточно, так как оно потеряет информацию о динамическом типе.

Некоторые говорят, что интерфейсы Go являются динамически типизированными, но это вводит в заблуждение. Они статически типизированы: переменная типа интерфейса всегда имеет один и тот же статический тип, и хотя во время выполнения значение, хранящееся в переменной интерфейса, может изменять тип, это значение всегда будет удовлетворять интерфейсу.

В общем случае, если вы хотите указать nil для значения типа interface, используйте явное значение nil, а затем вы можете проверить равенство nil. Наиболее распространенным примером является встроенный тип error , который является интерфейсом с одним методом. Всякий раз, когда нет ошибки, вы явно устанавливаете или возвращаете значение nil, а не значение какой-либо конкретной (неинтерфейсной) ошибки типа типа (что было бы очень плохой практикой, см. Демонстрацию ниже).

В вашем примере путаница возникает из фактов, что:

  • вы хотите иметь значение в качестве типа интерфейса (shower)
  • , но значение, которое вы хотите хранить в срезе не тип shower, а конкретный тип

Поэтому, когда вы помещаете *display тип в срез shower, будет создано значение интерфейса, которое представляет собой пару (значение; тип), где значение равно nil, а тип - *display. Значение внутри пары будет nil, а не значением самого интерфейса. Если вы поместите значение nil в срез, то значение интерфейса себя будет nil, а условием x == nil будет true.

Демонстрация

См. этот пример: Игровая площадка

type MyErr string

func (m MyErr) Error() string {
    return "big fail"
}

func doSomething(i int) error {
    switch i {
    default:
        return nil // This is the trivial true case
    case 1:
        var p *MyErr
        return p // This will be false
    case 2:
        return (*MyErr)(nil) // Same as case 1
    case 3:
        var err error // Zero value is nil for the interface
        return err    // This will be true because err is already interface type
    case 4:
        var p *MyErr
        return error(p) // This will be false because the interface points to a
                        // nil item but is not nil itself.
    }
}

func main() {
    for i := 0; i <= 4; i++ {
        err := doSomething(i)
        fmt.Println(i, err, err == nil)
    }
}

Выход:

0 <nil> true
1 <nil> false
2 <nil> false
3 <nil> true
4 <nil> false

В случае 2 указатель nil но сначала он преобразуется в тип интерфейса (error), поэтому создается значение интерфейса, которое содержит значение nil и тип *MyErr, поэтому значение интерфейса не является nil.

44
ответ дан Howl 20 August 2018 в 07:56
поделиться
  • 1
    это довольно ... большой провал не так ли? Как я могу написать библиотеку, которая предоставляет интерфейсы таким образом? Я мог бы заверить, что он работает правильно, если пользователи предоставляют правильные типы с нулевым значением. Я думаю, если x == (душ) (ноль) будет иметь смысл, но это просто шокирует .. – sharpner 19 March 2015 в 07:52
  • 2
    В этом нет ничего шокирующего. Вы возвращаете кусочек из двух значений интерфейса, оба не равны нулю. Один из этих значений не-nil-интерфейса содержит значение nil. Значение интерфейса имеет , чтобы быть не ноль, чтобы содержать что угодно, даже нуль. Вы можете исправить это, как предположил icza, или перепроектировать ваш API, например. не возвращая срез интерфейсов. – Volker 19 March 2015 в 08:06
  • 3
    @sharpner Вы можете предоставить библиотеку, которая использует / возвращает значения типов интерфейсов. Но если значение «отсутствует», return nil явно. – icza 19 March 2015 в 08:11
  • 4
    Я все еще пытаюсь обернуть голову тем, как перепроектировать api. Может быть, typedef что-то [] ливень доставит меня дальше или я пока не знаю. Но я действительно узнал что-то совершенно новое о сегодняшнем дне. Мой пример несколько упрощает реальную проблему. В финальной реализации пользователь моей библиотеки должен реализовать getWater () и поэтому может ошибаться. Так что, поскольку я должен был предположить, что это произойдет, мне нужно было убедиться, что в слое нет нильских значений. Я ожидал, что пользователи смогут возвращать указатель на структуры или структуры, которые реализовали интерфейс .... – sharpner 19 March 2015 в 08:42
  • 5
    Указатель nil не является недопустимым значением. Обратите внимание, что вы можете называть методы только для нулевых указателей: play.golang.org/p/Agd8eIwaKZ Попробуйте прочитать мое сообщение о nil-указателе и nil-интерфейсах, это может помочь: npf.io/ 2014/05 / интро-To-Go-интерфейсы / # toc_4 – Nate Finch 19 March 2015 в 18:59

Давайте рассмотрим интерфейс как указатель.

Скажем, у вас есть указатель a, и он равен нулю, и ничего не указывает.

var a *int // nil

Тогда у вас есть указатель b, и он указывает на a.

var b **int
b = &a // not nil

Посмотрите, что произошло? b указывает на указатель, который указывает на ничего. Так что даже если это указатель nil в конце цепочки, b указывает на что-то - это не ноль.

Если вы заглянете в память процесса, это может выглядеть так: это:

address | name | value
1000000 | a    | 0
2000000 | b    | 1000000

Видите? a указывает на адрес 0 (это означает, что он nil), а b указывает на адрес a (1000000).

То же самое относится к интерфейсам (за исключением того, что они немного в памяти ).

Как и указатель, интерфейс, указывающий на указатель на nil, не будет нить.

Здесь см. себя , как это работает с указателями и , как это работает с интерфейсами .

4
ответ дан Moshe Revah 20 August 2018 в 07:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: