В чем разница между преобразованием типов и утверждением типов?
Каковы основные различия между:
v = t.(aType) // type assertion
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{}
может содержать любое значение, потому что любой набор является надмножеством пустого набора.