Data Types & Constants (iota)

Reviewed & published by Brayan K

You know the four headline types already. Now you'll meet Go's full numeric family (sized ints, unsigned ints, floats), understand the real difference between byte, rune and string, and master iota — Go's elegant way to build enums and bit flags without typing a single number by hand.

Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ The Numeric Family

Plain int is 64 bits on modern machines and is what you'll use 95% of the time. But Go gives you precise control when you need it: int8/16/32/64 for signed whole numbers of a specific size, uint variants for unsigned (zero-and-up) values, and float32/float64 for decimals. Reach for the sized types when memory matters (huge slices) or when an external format demands an exact width.

package main

import (
    "fmt"
    "math"
)

func main() {
    // Go has a whole FAMILY of number types, not just "int".
    // The size in the name is how many BITS it uses to store the value.

    var small int8 = 127        // -128 .. 127        (8 bits)
    var big int64 = 9000000000  // huge whole numbers (64 bits)
    var count uint = 42         // uint = "unsigned": 0 and up, never negative

    // float32 vs float64 — float64 is the default and more precise.
    var ratio float64 = 22.0 / 7.0

    fmt.Println(small, big, count)        // 127 9000000000 42
    fmt.Printf("ratio = %.5f\n", ratio)   // ratio = 3.14286

    // Every numeric type has a documented limit you can ask for.
    fmt.Println("max int8:", math.MaxInt8) // max int8: 127
    fmt.Println("max int32:", math.MaxInt32) // max int32: 2147483647
}

Your turn — fill in the three blanks. Remember a colour channel is 0–255 (perfect for an unsigned 8-bit type), and you must convert before mixing sizes.

package main

import "fmt"

func main() {
    // 🎯 YOUR TURN — replace each ___ then run it.

    // 1) Declare an unsigned 8-bit type to hold a colour channel (0..255)
    var red ___ = 200       // 👉 uint8

    // 2) Declare a float64 for a temperature reading
    var temp ___ = 36.6     // 👉 float64

    // 3) Convert red (a uint8) to an int so it prints alongside others
    asInt := ___            // 👉 int(red)

    fmt.Printf("red=%d temp=%.1f total=%d\n", red, temp, asInt+10)

    // ✅ Expected output:  red=200 temp=36.6 total=210
}

2️⃣ Bytes, Runes & Strings

This trips up almost everyone coming from other languages. A Go string is a sequence of bytes, and len(s) counts those bytes — not characters. Because Go uses UTF-8, an accented or non-Latin character can take 2–4 bytes. A rune (an alias for int32) represents one real Unicode character, and ranging over a string decodes runes for you. A byte is an alias for uint8.

package main

import "fmt"

func main() {
    // A string is a read-only sequence of BYTES (UTF-8 encoded text).
    s := "Héllo"

    // len() counts BYTES, not characters. 'é' takes 2 bytes in UTF-8,
    // so this 5-letter word reports a length of 6.
    fmt.Println("bytes:", len(s)) // bytes: 6

    // A 'rune' is Go's name for a Unicode code point (a real character).
    // Ranging over a string yields runes, decoded correctly.
    runeCount := 0
    for range s {
        runeCount++
    }
    fmt.Println("runes:", runeCount) // runes: 5

    // A rune literal uses 'single quotes' and is really an int32.
    var letter rune = 'A'
    fmt.Println("rune:", letter)          // rune: 65 (its Unicode value)
    fmt.Printf("as char: %c\n", letter)   // as char: A

    // A byte is an alias for uint8 — one raw byte of data.
    var b byte = 'Z'
    fmt.Println("byte:", b) // byte: 90
}

3️⃣ Constants & iota

Go has no enum keyword. Instead it gives you iota — a counter that resets to 0 at the start of each const (...) block and increments by one on every line. Write the expression once and Go repeats it down the list. Pair it with a named type (like type Weekday int) and you get type-safe enums. With 1 << iota you get powers of two — perfect for bit flags you can combine with |.

package main

import "fmt"

// iota is Go's auto-incrementing counter for grouped constants.
// Inside a const block it starts at 0 and goes up by 1 each line.
type Weekday int

const (
    Sunday    Weekday = iota // 0
    Monday                   // 1  (the expression "= iota" repeats)
    Tuesday                  // 2
    Wednesday                // 3
    Thursday                 // 4
    Friday                   // 5
    Saturday                 // 6
)

// iota is great for bit flags: 1 << iota doubles each line.
const (
    Read    = 1 << iota // 1  (binary 001)
    Write               // 2  (binary 010)
    Execute             // 4  (binary 100)
)

func main() {
    fmt.Println("Wednesday is day", Wednesday) // Wednesday is day 3
    fmt.Println("Read|Write =", Read|Write)    // Read|Write = 3
    fmt.Println("Execute =", Execute)          // Execute = 4
}

Your turn — complete this log-level enum. The first line starts the counter; the rest follow automatically.

package main

import "fmt"

// 🎯 YOUR TURN — finish this enum of log levels with iota.
type Level int

const (
    Debug Level = ___   // 👉 iota  (starts the counter at 0)
    Info                // 1
    Warn                // 2
    Error               // 3
)

func main() {
    fmt.Println("Warn is level", Warn) // Warn is level 2

    // ✅ Expected output:  Warn is level 2
}

🧩 Reorder Challenge

These lines define a bit-flag constant block, but they're scrambled. Put them in the correct order so the program compiles and prints Write = 2 (assume a func main wraps the print).

    fmt.Println("Write =", Write)
)
const (
    Read = 1 << iota
    Write
package main; import "fmt"

Why: the package and import come first. The const ( block must open before its members. Read = 1 << iota sets the pattern on line 0 (value 1), and Write on the next line repeats it at iota=1, giving 1 << 1 = 2. The block must be closed with ) before any code references Write.

Pro Tips

Common Errors (and the fix)

📋 Quick Reference

TypeMeaningRange / Note
int / int64Signed whole number±9.2 quintillion
int8Tiny signed int-128 .. 127
uint8 / byteUnsigned 8-bit0 .. 255
rune / int32One Unicode char'A' == 65
float64Decimal (default)~15 digits precision
iotaConst counter0,1,2,… per block

🧠 Quick Recall

Predict the output before you reveal the answer.

5 — the é is two bytes in UTF-8, so the 4-character word is 5 bytes long. Count characters with utf8.RuneCountInString to get 4.

const ( A = iota; B; C ); fmt.Println(C)

2 — iota is 0 for A, 1 for B, 2 for C. The = iota expression repeats implicitly on each line.

B — Unicode code point 66 is the capital letter B, and %c prints the character for that value.

Frequently Asked Questions

Q: Should I ever use int32 instead of int?

Rarely for everyday counters. Use a sized type when you're matching a binary file format, a network protocol, or trimming memory in a very large slice. Otherwise plain int is idiomatic and fast.

Q: Why does len on a string give a strange number?

len counts bytes, and UTF-8 uses multiple bytes for non-ASCII characters. To count characters, range over the string (which yields runes) or call utf8.RuneCountInString.

Q: Can I make iota skip a value?

Yes — assign that line to the blank identifier _. A common pattern is _ = iota on the first line so your real values start at 1.

Mini-Challenge: Planet Enum

Bring it together: an iota enum plus a float64 for mass. Write it yourself and match the example output in the comments.

package main

import "fmt"

func main() {
    // 🎯 MINI-CHALLENGE: Planet sizes
    // 1. Use iota to define an enum of Planets (Mercury..Mars, 0..3).
    // 2. Store a planet's mass in a float64 (e.g. Earth = 5.97).
    // 3. Print: "Earth is planet number 2 with mass 5.97e24 kg".
    //    (Hint: %g prints a float compactly; you can write 5.97e24.)
    //
    // ✅ Example output:
    //    Earth is planet number 2 with mass 5.97e+24 kg

    // your code here
}

🎉 Lesson Complete!

Practice quiz

What is the range of an int8?

  • 0 to 255
  • 0 to 127
  • -128 to 127
  • -256 to 255

Answer: -128 to 127. A signed 8-bit integer (int8) holds values from -128 to 127.

What does len("café") return in Go?

  • 5
  • 4
  • 3
  • 8

Answer: 5. len counts bytes, and é is two bytes in UTF-8, so the 4-character word is 5 bytes long.

A rune in Go is an alias for which type?

  • uint8
  • int64
  • byte
  • int32

Answer: int32. A rune is an alias for int32 and represents a single Unicode code point.

A byte in Go is an alias for which type?

  • int8
  • uint8
  • int32
  • rune

Answer: uint8. byte is an alias for uint8 — one raw, unsigned 8-bit value.

What does fmt.Printf("%c", rune(66)) print?

  • B
  • 66
  • A
  • b

Answer: B. Unicode code point 66 is the capital letter B, and %c prints the character for that value.

Which floating-point type is Go's default and more precise?

  • float32
  • float16
  • float64
  • decimal

Answer: float64. float64 is the default float type in Go and offers roughly 15 digits of precision.

What is the value of letter in: var letter rune = 'A'; fmt.Println(letter)?

  • A
  • 65
  • 0
  • 'A'

Answer: 65. A rune is an integer type, and the Unicode value of 'A' is 65, which is what Println shows.

Given const ( A = iota; B; C ), what does C equal?

  • 0
  • 1
  • 3
  • 2

Answer: 2. iota is 0 for A, 1 for B, and 2 for C — the = iota expression repeats implicitly each line.

Will two different integer types like int and int64 mix automatically in an expression?

  • Yes
  • No — you must convert explicitly
  • Only if same size
  • Only with floats

Answer: No — you must convert explicitly. Go never mixes numeric types implicitly; you must convert, e.g. int64(x) + y.

How should you count the actual characters in a UTF-8 string?

  • len(s)
  • cap(s)
  • range over the string or utf8.RuneCountInString(s)
  • s.length

Answer: range over the string or utf8.RuneCountInString(s). len counts bytes; range over the string yields runes, or use utf8.RuneCountInString to count characters.

Continue this course