What is the difference between type conversion and type assertion?

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 ?


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.


tl;dr assertions operate on interfaces' dynamic values and are evaluated at run-time, whereas conversions operate on static types and are evaluated at compile time.

Type Assertion

You know that a Go 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 as 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. As an example, Context values:

func foo(ctx context.Context) {
    s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
    // do something with s...
}

Because information about the value stored in the interface is available only at run-time, an unchecked type assertion can panic — you must use the special form 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. Probably the biggest difference with the assertion is that a type conversion is statically checked2. An invalid conversion will be caught at compile time:

    type Foo string
    type Bar int

    a := "foo"
    fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar

The rules that govern type conversions also cover many more cases than type assertions. The main one is assignability, but you have special cases for conversions between numerical types, strings and byte/rune slices, directed channels, slices and array pointers, etc.

In general, you use a conversion when you already know what are the types involved, and simply want to change one into 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.