Skip to content
Theory · Interfaces · lesson 5 of 10

any and type assertions

In the theme's first lesson an interface was a job ad. What if the ad lists no requirements at all? Then any candidate fits. But the employer also knows nothing about them: before handing the newcomer the wheel, they have to ask, "Are you a driver?"

In Go, such an ad with no requirements is the type any. A variable of type any can hold a value of any type: a number, a string, a slice. But you cannot treat that value as a number, even when a number is inside:

GO
package main

import "fmt"

func main() {
    var v any = 42
    fmt.Println(v + 1) // ✗
}
TEXT
invalid operation: v + 1 (mismatched types any and untyped int)

The compiler knows only the type of the variable v, and that is any. That v will hold an int at run time is not known at build time.

The empty interface

any is another name for the type interface{}, an interface with no methods at all. Such an interface is called the empty interface. Every type meets its requirements, so any accepts any value. Two examples from practice:

  • fmt.Println takes arguments of type any and therefore prints anything;
  • a slice []any holds values of different types mixed together.

The dynamic type

An interface value remembers not only the value but also its concrete type. That type is called the dynamic type, and the %T verb prints it:

GO
var v any = 42
fmt.Printf("%T\n", v) // int
v = "go"
fmt.Printf("%T\n", v) // string

Let's walk through the example line by line:

  1. At first v holds 42, and %T prints int.
  2. After v = "go" the same variable holds a string, and %T prints string.

The type of the variable v itself does not change: it is still any. Only the dynamic type of the value inside changes.

Interfaces and nil

So an interface value is a pair: the dynamic type and the value itself. An interface equals nil only when both parts are empty.

Interviewers like to ask this: is an interface value nil if you put a nil pointer into it? Let's go step by step:

  1. var p *int creates an empty pointer, and p == nil gives true.
  2. var v any = p puts that pointer into an interface.
  3. v == nil gives false: the pointer inside is empty, but the dynamic type *int is already recorded in the pair.

Press the button and watch when v == nil is true and when it is not.

LiveInterface value: type and valuestep 0 of 3 · v == nil → true
var v any
var p *int // nil
x := 5

The pair v is empty so far: no type, no value. Press the button.

Watch the type box

Type assertion

The type assertion v.(int) takes a value of type int out of an interface. This is the question "Are you a driver?". To use it without surprises, remember its rules:

  • if the dynamic type of v really is int, the result is a plain number that arithmetic works with;
  • an assertion converts nothing; it only checks the type, so the string "42" does not become a number;
  • it applies only to a value of an interface type, never to a variable of type int.

Here the job-ad picture stops working. A real candidate may answer "almost a driver", but a type assertion demands an exact match. For a v holding an int, the check v.(float64) fails, even though both types are numbers.

An assertion has two forms. Here are both in one example:

GO
var v any = 42
n := v.(int)
fmt.Println(n + 1) // 43
s, ok := v.(string)
fmt.Printf("%q %v\n", s, ok) // "" false

The one-result form

The statement n := v.(int) returns only the value. Here v holds an int, so n gets the number 42, and n + 1 prints 43.

This form fits only when the type is known for certain. If the type does not match, the program ends in a panic—the common mistake section shows it.

The two-result form

The statement s, ok := v.(string) returns the value and a flag, like reading from a map. Let's see what happens in the example:

  1. The assertion checks whether v holds a string.
  2. It holds an int, not a string, so ok is false.
  3. The variable s gets the zero value of type string, the empty string "", and the program goes on.

When the type matches, ok is true and s holds the string itself. You write this form when the value may be of another type.

Common mistake

The one-result assertion was applied to a value of another type:

GO
var v any = "42"
n := v.(int) // ✗
fmt.Println(n)
TEXT
panic: interface conversion: interface {} is string, not int

The program compiles but ends in a panic at run time: v holds a string, not an int. The one-result form fits only when the type is known for certain. When the value may be of another type, write n, ok := v.(int) and check ok.

Key points

  • any is the empty interface interface{}: it holds a value of any type.
  • An interface value is a pair of a dynamic type and a value; with a nil pointer inside, it is not equal to nil.
  • v.(int) only checks the type and converts nothing; on a mismatch, the one-result form panics.
  • The form s, ok := v.(string) does not panic: on failure ok is false and s gets the zero value.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way