57

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 ?

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Salah Eddine Taouririt
  • 24,925
  • 20
  • 60
  • 96
  • 1
    Also a bit off-topic but worth noting, casting generally implies some sort of runtime support, but assertions are purely a compile time construct and a way to provide the compiler hints on how you want your code to be analysed. – Leo Jan 15 '19 at 10:55

2 Answers2

67

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.

cmcginty
  • 113,384
  • 42
  • 163
  • 163
Volker
  • 40,468
  • 7
  • 81
  • 87
  • "and t will be an aType" - Can you elaborate on that? `t` is not modified? – nemo Dec 11 '13 at 15:28
  • @nemo I think he would say `v`, not `t` – LucianoQ Sep 25 '17 at 13:30
  • @LucianoQ: No, it's `t` not `v`. @nemo: No `t` is not modified by a type assertion or a type conversion. I strongly recommend the Tour of Go and the language spec afterwards. – Volker Sep 25 '17 at 14:02
  • @Volker thanks for the recommendation, it is good lecture! However, I still think that you meant `v` and not `t`. `t` in this case is will be some interface type, e.g. `interface{}` and after the assertion *on* `t`, the *resulting* value `v` will be of type `aType`. – nemo Sep 26 '17 at 09:12
  • @nemo. No. I mean that the dynamic type of t is aType. The type of v is uninteresting. – Volker Sep 26 '17 at 09:40
  • Yes, it is. But you never said that in the answer. – nemo Sep 26 '17 at 16:07
  • @nemo The go to reference for this stuff is the language spec and not my answer. – Volker Sep 26 '17 at 18:43
  • 1
    OK, let me try to rephrase the initial question so we stop misunderstanding each other. In your answer you wrote: "A type assertion asserts that t (an interface type) actually is a aType and t will be an aType; " . This sentence redundantly states that `t` is of type `aType`, hence why I asked whether you meant `v` in the second part of the sentence. This is also what @LucianoQ meant. I could just edit your answer but I thought I ask to clear up any misunderstanding beforehand. – nemo Sep 26 '17 at 20:37
  • @nemo I have to admit I do not see much value in this discussion. The static type of an interface is pretty much uninteresting. – Volker Sep 26 '17 at 20:46
4

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 set1.

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.
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 checked2. 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

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.

blackgreen
  • 34,072
  • 23
  • 111
  • 129