В чем разница между преобразованием типов и утверждением типов?

Каковы основные различия между:

  1. v = t.(aType) // type assertion
  2. v = aType(t) // type conversion

Где я должен использовать утверждение типа или преобразование типа?

2 ответа

Решение

Утверждение типа утверждает, что t (тип интерфейса) на самом деле является aType а также t будет aType; а именно тот, завернутый в t интерфейс. Например, если вы знаете, что ваш var reader io.Reader на самом деле это *bytes.Buffer ты можешь сделать var br *bytes.Buffer = reader.(*bytes.Buffer),

Преобразование типов преобразует один (неинтерфейсный) тип в другой, например var x uint8 и Int64, как var id int64 = int64(x),

Практическое правило. Если вам нужно обернуть конкретный тип в интерфейс и хотите вернуть конкретный тип, используйте утверждение типа (или переключатель типа). Если вам нужно преобразовать один конкретный тип в другой, используйте преобразование типов.

Утверждения tl; dr работают с динамическими значениями интерфейсов и оцениваются во время выполнения, тогда как преобразования работают со статическими типами и оцениваются во время компиляции.

Утверждение типа

Вы знаете, что интерфейс Go - это в основном спецификация набора методов, и вы можете присвоить переменной интерфейса любое значение, тип которого реализует этот набор методов 1.

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

Одно из наиболее распространенных применений - это когда у вас есть и вам нужно получить конкретное значение. В качестве примера,Context ценности:

      func foo(ctx context.Context) {
    s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
    // do something with s...
}

Поскольку информация о значении, хранящемся в интерфейсе, доступна только во время выполнения, непроверенное утверждение типа может вызвать панику - вы должны использовать назначение специальной формы v, ok := x.(T) чтобы избежать этого.

      ctx = context.WithValue(ctx, "my_key", "foo")
s := ctx.Value("my_key").(int) // panic

v, ok := ctx.Value("my_key").(string)
fmt.Println(v, ok) // "foo" true

Кроме того, когда в x.(T)является самим интерфейсом, утверждение проверяет, что значение, хранящееся в, реализует. Результат такой же, как и выше.

Преобразование типов

Преобразование типа записывается в виде T(x)вместо этого «изменяет тип выражения на тип, указанный при преобразовании» , т. е. изменяет тип x к T. Вероятно, самая большая разница с утверждением состоит в том, что преобразование типа проверяется статически . Неверное преобразование будет обнаружено во время компиляции:

          type Foo string
    type Bar int

    a := "foo"
    fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar

Правила, управляющие преобразованием типов, охватывают гораздо больше случаев, чем утверждения типа. Главный из них - возможность присваивания , но у вас есть особые случаи для преобразований между числовыми типами, строками и срезами байтов / рун, направленными каналами, срезами и указателями на массивы и т. Д.

В общем, вы используете преобразование, когда уже знаете, какие типы задействованы, и просто хотите заменить один на другой:

      b := []byte("foo")  // converts string literal to byte slice

Примечания:

1: более формально, когда набор методов значения является надмножеством набора методов интерфейса; вот почему пустой интерфейс interface{} может содержать любое значение, потому что любой набор является надмножеством пустого набора.

Другие вопросы по тегам