December Code

Go data types interview questions, with answers

Go's type system is small and strict, and interviewers use that strictness to find out whether you have written real Go or only read about it. The questions sit on a handful of rules: which integer sizes are fixed and which depend on the platform, how the short declaration differs from var, what a variable holds before you assign it, why a constant can be used where a variable of the same value cannot, and why Go never converts one numeric type into another on its own.

Each answer below comes with code, and every output shown was produced by building and running it with Go 1.27. The questions move from the types themselves to declarations, constants and the conversions and scoping rules behind the common mistakes. Then take the free Go diagnostic — ten questions across every Go topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.What are the basic data types in Go, and how big is an int?

    In short: Go has bool, string, sized integers and floats, complex numbers, and the aliases byte and rune; int and uint are 32 or 64 bits, depending on the platform.

    Go's numeric types mostly carry their size in the name: int8 to int64, the unsigned uint8 to uint64, float32 and float64, and complex64 and complex128. int and uint are the exception: they are 32 bits on 32-bit platforms and 64 bits on 64-bit ones, which covers every mainstream system today, and strconv.IntSize reports which. byte is an alias for uint8, used for raw data, and rune is an alias for int32, used for a Unicode code point, so a character literal such as 'é' is a rune with the value 233. string is an immutable sequence of bytes and bool is true or false. Use int for counts and indexes, and a sized type only when the width is part of a format.

    fmt.Println(strconv.IntSize)
    // 64
    var b byte = 'A'
    var r rune = 'é'
    fmt.Println(b, r)
    // 65 233
  2. 2.What is the difference between var, := and const in Go?

    In short: var declares a variable anywhere, := declares and initialises one inside a function with an inferred type, and const declares a value fixed at compile time.

    var name T = value is the general form. It works at package level and inside functions, and either the type or the value may be left out, but not both. The short declaration name := value is allowed only inside functions and always infers the type from the value; it must declare at least one new variable on its left, which is why a second a, err := f() in the same scope fails but b, err := g() compiles, reusing err. const declares a value fixed when the program is compiled: a number, string, rune or bool, never a slice, map or struct, and it can never be assigned to. So package-level state always uses var, and := is the everyday form inside functions.

    const limit = 3
    var total int
    for i := 1; i <= limit; i++ {
        total += i
    }
    fmt.Println(total)
    // 6
  3. 3.What is a zero value in Go?

    In short: Every variable declared without a value starts at its type's zero value: 0, false, the empty string, nil for reference-like types, and all-zero fields for a struct.

    Go never hands you uninitialised memory. A variable declared with var and no value, a field of a new struct and an element of a freshly made slice all hold their type's zero value: 0 for numbers, false for bool, the empty string for string, and nil for pointers, slices, maps, channels, functions and interfaces. Structs and arrays are zeroed field by field, as the Point below shows. Idiomatic Go types are designed so that the zero value is ready to use: a zero sync.Mutex is an unlocked mutex and a zero bytes.Buffer is an empty buffer, with no constructor call needed. Reading a zero value is always safe; what differs between types is which operations they support while still zero.

    type Point struct {
        X, Y float64
        Tag  string
    }
    var p Point
    fmt.Println(p.X, p.Tag == "")
    // 0 true
  4. 4.What are untyped constants in Go?

    In short: A constant declared without a type keeps its exact value and gets a type only where it is used, so it can mix with any compatible type without a conversion.

    const huge = 1 << 100 declares an untyped constant. The compiler keeps it as an exact number of any size and gives it a type only at the point of use, choosing whatever the context needs, so one constant can be used as an int, a float64 or a time.Duration. Constant arithmetic therefore cannot overflow: 1 << 100 is legal as long as the value finally used fits its variable, and huge >> 98 below is just 4. A typed constant, such as const n int64 = 5, behaves like an int64 and needs a conversion to mix with other types, exactly as a variable would. The flexibility ends as soon as a value is stored in a variable, which always has one fixed type.

    const huge = 1 << 100
    fmt.Println(huge >> 98)
    // 4
    const half = 0.5
    var d float64 = half
    fmt.Println(d * 3)
    // 1.5
  5. 5.How do you convert between types in Go?

    In short: Numbers are converted explicitly with T(v), text with the strconv package; Go never converts implicitly, and string(n) on an integer gives a character, not digits.

    Go has no implicit conversions between different types, not even from int32 to int64 or from int to float64, so mixed arithmetic needs an explicit conversion such as float64(n). Converting a float to an integer truncates toward zero, so int(-33.5) is -33. Numbers and text are converted with strconv: Itoa and Atoi for ints, ParseFloat and FormatFloat for floats, with the parsing functions returning an error for bad input. The classic trap is string(n) on an integer, which treats n as a Unicode code point and yields a one-character string: string(rune(67)) is C, not 67. go vet reports string(n) on a plain int for exactly this reason.

    n := 67
    f := float64(n) / 2
    fmt.Println(f, int(-f))
    // 33.5 -33
    fmt.Println(strconv.Itoa(n))
    // 67
    fmt.Println(string(rune(n)))
    // C
  6. 6.What is iota, and how do you use it for enumerations?

    In short: iota is a counter inside a const block that starts at 0 and rises by one on each line, so a block of constants can number itself.

    Go has no enum keyword. Instead, inside a parenthesised const block, iota is the index of the current line, starting at 0, and a constant written without an expression repeats the one above it with its own iota. Giving the constants a named type, such as Lvl below, stops them being mixed up with ordinary ints, and a String method makes fmt print them as names instead of numbers. The blank identifier _ skips a value, and 1 << iota produces bit flags. iota restarts at 0 in every const block, so each block numbers its own constants. Converting back with int(High) gives the underlying number.

    type Lvl int
    
    const (
        Low Lvl = iota
        Mid
        High
    )
    
    var names = []string{
        "low", "mid", "high",
    }
    
    func (l Lvl) String() string {
        return names[l]
    }
    
    func main() {
        n := int(High)
        fmt.Println(High, n)
        // high 2
    }
  7. 7.What is variable shadowing in Go, and why is it risky with err?

    In short: Shadowing is declaring a new variable with the same name in an inner scope, usually with :=, so assignments inside the block no longer reach the outer variable.

    Every block in Go, including the bodies of if, for and switch, opens a new scope, and := in that scope declares a new variable even when an outer one has the same name. The classic bug is with err: the outer err is declared, an inner block calls _, err := strconv.Atoi(...), and the failure lands in the inner err, which disappears at the closing brace. The outer err is still nil, so the function carries on as if nothing went wrong, which is what the code below prints. The fixes are to declare the variable first and assign with =, or to handle the error inside the block. Linters such as the shadow analyser look for this pattern.

    var err error
    if true {
        _, err := strconv.Atoi("x")
        _ = err
    }
    fmt.Println(err)
    // <nil>

How the diagnostic asks it

One question from the Go bank, exactly as a sitting would show it. The bank has 4 on types & variables and 30 across Go.

Types & Variables · mediumGO-003

What does this Go code print?

const (
    A = iota * 10
    B
    _
    D
)
fmt.Println(A, B, D)
  1. 10 1 3
  2. 20 10 30correct
  3. 30 10 20
  4. 4It does not compile: B and D have no value

Inside a parenthesised const block, iota is the index of the line: 0 for A, 1 for B, 2 for the blank identifier and 3 for D. A constant written without an expression repeats the previous expression, so B, _ and D are all iota * 10, evaluated with their own iota: 10, 20 (discarded by _) and 30. So the output is 0 10 30. 0 1 3 treats the omitted lines as plain iota, dropping the * 10. 0 10 20 forgets that _ still uses up a value of iota. It compiles: repeating the previous expression is exactly how Go builds enumerations, as in the standard library's time.Month and similar constants.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 Go questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated