суббота, 26 января 2019 г.

Эффективный Go: преобразования интерфейсов и утверждения типа

Переключатели типов (type switch) являются формой преобразования: они принимают интерфейс и, для каждого case в switch, в некотором смысле, преобразовывают его в тип этого case. Вот упрощенная версия того, как код в fmt.Printf превращает значение в строкe с использованием переключателя типа. Если это уже строка, мы хотим, чтобы фактическое значение строки содержалось в интерфейсе, тогда как если оно имеет метод String мы хотим получить результат вызова метода.

type Stringer interface {
    String() string
}

// Значение, предоставленное вызывающим.
var value interface{} 
switch str := value.(type) {
case string:
    return str
case Stringer:
    return str.String()
}

Первый case находит конкретное значение; второй преобразует интерфейс в другой интерфейс.

Что если есть только один тип, который нас интересует? Если мы знаем, что значение содержит string а мы просто хотим его извлечь? Переключатель типа с одним case подойдет, как и утверждение типа. Утверждение типа принимает значение интерфейса и извлекает из него значение указанного явного типа. Синтаксис заимствует из условия, открывающего переключатель типа, но с явным типом вместо ключевого слова type:

value.(typeName)

и результатом является новое значение со статическим типом typeName. Этот тип должен быть конкретным типом, поддерживаемым интерфейсом, или вторым интерфейсом типа, в который может быть преобразовано значение. Чтобы извлечь строку, которую, как нам известно, находится в значении, мы могли бы написать:

str := value.(string)

Но если окажется, что значение не содержит строку, программа завершится с ошибкой во время выполнения. Чтобы избежать этого, используйте идиому «comma, ok», чтобы безопасно проверить, является ли значение строкой:

str, ok := value.(string)
if ok {
    fmt.Printf("string value is: %q\n", str)
} else {
    fmt.Printf("value is not a string\n")
}

Если утверждение типа не выполнено, str все еще будет существовать и иметь тип string, но будет иметь нулевое значение, пустую строку.

В качестве иллюстрации возможности приведем if-else утверждение, которое эквивалентно переключателю типа, который мы привели в начале поста.

if str, ok := value.(string); ok {
    return str
} else if str, ok := value.(Stringer); ok {
    return str.String()
}


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


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

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