英文:
What is the difference between type conversion and type assertion?
问题
v = t.(aType) 是类型断言(type assertion),用于将接口类型的变量 t 转换为指定的类型 aType。如果 t 的底层类型是 aType,则 v 将持有该底层值;否则,将产生一个运行时错误。
v = aType(t) 是类型转换(type conversion),用于将一个值 t 转换为指定的类型 aType。这种转换只能在兼容的类型之间进行,例如将一个整数转换为浮点数类型。
你应该在以下情况下使用类型断言或类型转换:
- 当你知道一个接口类型的变量实际上持有某个具体类型的值时,可以使用类型断言来获取该值。
- 当你需要将一个值从一种类型转换为另一种类型时,可以使用类型转换。
请注意,类型断言和类型转换都可能引发运行时错误,因此在使用之前应该确保类型转换是安全的。
英文:
What is the main differences between :
v = t.(aType) // type assertionv = aType(t) // type conversion
Where should I use type assertion or type conversion ?
答案1
得分: 67
类型断言(type assertion)断言了t(一个接口类型)实际上是aType类型,并且t将会是aType类型;也就是说,它是包装在t接口中的aType类型。例如,如果你知道你的var reader io.Reader实际上是一个*bytes.Buffer,你可以这样做:var br *bytes.Buffer = reader.(*bytes.Buffer)。
类型转换(type conversion)将一个(非接口)类型转换为另一个类型,例如将var x uint8转换为int64,可以这样写:var id int64 = int64(x)。
经验法则:如果你需要将具体类型包装到接口中,并且想要还原为具体类型,请使用类型断言(或类型切换)。如果你需要将一个具体类型转换为另一个具体类型,请使用类型转换。
英文:
A type assertion asserts that t (an interface type) actually is a aType and t will be an aType; namely the one wrapped in the t interface. E.g. if you know that your var reader io.Reader actually is a *bytes.Buffer you can do var br *bytes.Buffer = reader.(*bytes.Buffer).
A type conversion converts one (non-interface) type to another, e.g. a var x uint8 to and int64 like var id int64 = int64(x).
Rule of thumb: If you have to wrap your concrete type into an interface and want your concrete type back, use a type assertion (or type switch). If you need to convert one concrete type to an other, use a type conversion.
答案2
得分: 4
tl;dr x.(T) 断言接口 x 的动态值在运行时是 T 类型;T(x) 将表达式 x 的类型转换为其他类型。
类型断言
你知道在 Go 中,接口基本上是一个方法集规范,你可以将任何实现该方法集的类型赋值给接口变量<sup>1</sup>。
type assertion 写作 x.(T),断言接口 x 中存储的值是类型 T。当你想要解包该值时,你会使用类型断言。
其中最常见的用法之一是当你有一个 interface{} 类型的变量,并且你需要检索它存储的具体值。一个典型的例子是 Context 值:
func foo(ctx context.Context) {
s := ctx.Value("my_key").(string) // 签名为 `Value(key interface{}) interface{}`
// 使用 s 做一些操作...
}
它被称为断言,因为在编译时不知道 x 是否实际上持有具体类型 T,但你断言它持有该类型。这就是为什么未经检查的断言 y := x.(T) 如果 x 实际上不持有 T 类型,就会引发 panic —— 你必须使用逗号-ok赋值 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 中存储的值是否实现了 T。结果与上述相同。
类型转换
type conversion 写作 T(x),它将表达式 x 的类型更改为指定的类型,即将 x 的类型更改为 T。转换的一个重要属性是它们是静态检查的<sup>2</sup>。无效的转换将无法编译通过:
type Foo string
type Bar int
a := "foo"
fmt.Println(Bar(a)) // 无法将 a (类型为 string) 转换为类型 Bar
转换有效的主要条件是涉及的类型之间的可赋值性,但还有其他几个条件,包括数值类型、字符串和字节/符文切片、定向通道、切片和数组指针等之间的转换。
简单来说,当你已经知道涉及的类型,并且只想将一个类型更改为另一个类型时,你可以使用转换:
b := []byte("foo") // 将字符串字面量转换为字节切片
<hr>
注释:
1:更正式地说,当值的方法集是接口方法集的超集时;这也是为什么空接口 interface{} 可以持有任何值,因为任何集合都是空集的超集。
2:当 x.(T) 中的类型 T 不实现接口时,类型断言也会在编译时进行检查。实际上,当 x 是 interface{} 时,所有类型都实现它,因此这不会帮助你捕捉错误。
英文:
tl;dr x.(T) asserts that the dynamic value of interface x is T at run time; T(x) converts the type of an expression x to some other type.
Type Assertion
You know that in Go an interface is basically a method set specification, and you can assign to an interface variable any value whose type implements that method set<sup>1</sup>.
The type assertion written x.(T) asserts that the value stored in the interface x is of type T. You use a type assertion when you want to unbox that value.
One of the most common uses is when you have interface{} and you need to retrieve the concrete value it stores. A typical example, Context values:
func foo(ctx context.Context) {
s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
// do something with s...
}
It is called assertion because at compile time it is not known whether x actually holds the concrete type T, but you assert that it does.<br> That's why the unchecked assertion y := x.(T) panics if x doesn't actually hold a T — you must use the comma-ok assignment v, ok := x.(T) to avoid it.
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
In addition, when T in x.(T) is an interface itself, the assertion checks that the value stored in x implements T. The outcome is the same as above.
Type Conversion
A type conversion written as T(x) instead "changes the type of an expression to the type specified by the conversion", i.e. changes the type of x to T. An important property of conversions is that they are statically checked<sup>2</sup>. An invalid conversion simply won't compile:
type Foo string
type Bar int
a := "foo"
fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar
The main condition for a conversion to be valid is assignability between the types involved, but there's several more, including conversions between numerical types, strings and byte/rune slices, directed channels, slices and array pointers, etc.
In simple terms, you use a conversion when you already know what are the types involved, and simply want to change one to the other:
b := []byte("foo") // converts string literal to byte slice
<hr>
Notes:
1: more formally, when the value's method set is a superset of the interface method set; this is also why the empty interface interface{} can hold any value, because any set is a superset of an empty set.
2: type assertions are also checked at compile time when the type T in x.(T) does not implement the interface. In practice, this won't help you catch errors when x is interface{} since all types implement it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论