类型转换和类型断言之间有什么区别?

huangapple go评论88阅读模式
英文:

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 :

  1. v = t.(aType) // type assertion
  2. 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 不实现接口时,类型断言也会在编译时进行检查。实际上,当 xinterface{} 时,所有类型都实现它,因此这不会帮助你捕捉错误。

英文:

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(&quot;my_key&quot;).(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, &quot;my_key&quot;, &quot;foo&quot;)
s := ctx.Value(&quot;my_key&quot;).(int) // panic

v, ok := ctx.Value(&quot;my_key&quot;).(string)
fmt.Println(v, ok) // &quot;foo&quot; 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 := &quot;foo&quot;
	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(&quot;foo&quot;)  // 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.

huangapple
  • 本文由 发表于 2013年12月10日 20:13:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/20494229.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定