понедельник, 11 февраля 2019 г.

Go FAQ: Почему nil значение ошибки не равно nil?

Под капотом интерфейсы реализованы в виде двух элементов, типа T и значения V. V - это конкретное значение, такое как int, struct или указатель, никогда не сам интерфейс, и имеет тип T. Например, если мы храним int значение 3 в интерфейсе, результирующее значение интерфейса имеет, схематически, (T = int, V = 3). Значение V также известно как динамическое значение интерфейса, поскольку данная переменная интерфейса может содержать разные значения V (и соответствующие типы T) во время исполнения программы.

Значение интерфейса равно nil, только если V и T оба не заданы (T = nil, V не задано), в частности, интерфейс nil всегда будет содержать тип nil. Если мы сохраним указатель nil типа *int внутри значения интерфейса, внутренний тип будет *int независимо от значения указателя: (T = *int, V = nil). Следовательно, такое значение интерфейса будет не nil, даже если значение указателя V внутри равно nil.

Эта ситуация может сбивать с толку и возникает, когда значение nil хранится внутри значения интерфейса, такого как возвращаемый error:

func returnsError() error {
    var p *MyError = nil
    if bad() {
        p = ErrBad
    }
    return p // Всегда будет возвращать не-nil ошибку.
}

Если все идет хорошо, функция возвращает nil p, поэтому возвращаемое значение является значением интерфейса error содержащее (T = *MyError, V = nil). Это означает, что если вызывающая сторона сравнивает возвращенную ошибку с nil, это всегда будет выглядеть так, как будто произошла ошибка, даже если ничего плохого не произошло. Чтобы вернуть правильную nil error вызывающей стороне, функция должна возвращать явный nil:

func returnsError() error {
    if bad() {
        return ErrBad
    }
    return nil
}

Это хорошая идея для функций, которые возвращают ошибки всегда использовать тип error в их сигнатуре (как мы сделали выше), а не конкретный тип, такой как *MyError, чтобы гарантировать, что ошибка создана правильно. В качестве примера, os.Open возвращает error, хотя, если не nil, она всегда конкретного типа *os.PathError.

Ситуации, подобные описанным здесь, могут возникать всякий раз, когда используются интерфейсы. Просто имейте в виду, что если какое-то конкретное значение было сохранено в интерфейсе, интерфейс не будет nil.


Читайте также:


Комментариев нет:

Отправить комментарий