英文:
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 assertion
v = 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论