5. The basic types

📖 Reading · 8 min
💡 Every code box below is live — edit it and hit Run.

Go is statically typed: every value has a type, fixed when it's created, checked before your program runs. The set of built-in types is small and sharp. Here are the ones you'll use daily.

Integers

Whole numbers. The plain int is what you'll use 95% of the time — it's the "natural" size for the machine (64 bits on modern computers). When you need a specific size, Go has explicit widths:

  • Signed: int8, int16, int32, int64 (and int)
  • Unsigned (non-negative only): uint8, uint16, uint32, uint64, uint
package main

import "fmt"

func main() {
    var big int = 9000000000
    var small int8 = 127 // max for int8; 128 would overflow

    fmt.Println(big, small)
    fmt.Printf("%T %T\n", big, small)
}

Two aliases you'll meet: byte is another name for uint8 (a raw byte), and rune is another name for int32 (a single Unicode character). More on those when we reach strings.

Floating-point

Numbers with a fractional part. Use float64 unless you have a specific reason not to — it's the default and the most precise.

package main

import "fmt"

func main() {
    price := 19.99
    half := price / 2
    fmt.Printf("%.2f split in two is %.3f each\n", price, half)
}

A caution that applies to every language: floats are approximate. 0.1 + 0.2 is not exactly 0.3. Run it and see — this is normal, not a Go quirk:

package main

import "fmt"

func main() {
    fmt.Println(0.1 + 0.2)
}

For money, you generally work in integer cents rather than floats to avoid this. We'll return to it.

Booleans

bool is either true or false — nothing else, and no automatic conversion from numbers. In Go, 1 is not truthy and 0 is not falsy; a condition must be an actual bool.

package main

import "fmt"

func main() {
    isReady := true
    hasErrors := 3 > 5

    fmt.Println(isReady, hasErrors)
    fmt.Println(isReady && !hasErrors)
}

&& is "and", || is "or", ! is "not". Comparisons (>, <, ==, !=, >=, <=) all produce a bool.

Strings

A string is an immutable sequence of bytes, almost always holding UTF-8 text. Double quotes make a string; backticks make a raw string that keeps newlines and ignores escapes:

package main

import "fmt"

func main() {
    greeting := "Hello,\n\tGo" // \n and \t are interpreted
    raw := `Hello,\n\tGo`      // backticks: literally backslash-n

    fmt.Println(greeting)
    fmt.Println(raw)
}

Join strings with +, and get the length in bytes with len:

package main

import "fmt"

func main() {
    first := "Go"
    full := first + "lang"
    fmt.Println(full, "has", len(full), "bytes")
}

(For non-ASCII text, byte length and character count differ — a topic for the strings lesson. For plain ASCII they're the same.)

Checking a type

When you're unsure, %T tells you exactly what you've got. Untyped literals default to int for whole numbers and float64 for decimals:

package main

import "fmt"

func main() {
    fmt.Printf("%T %T %T %T\n", 42, 3.14, true, "hi")
}

Your turn

A rectangle is width = 7 and height = 3 (both int). Print exactly:

Area: 21, Perimeter: 20
package main

import "fmt"

func main() {
    width := 7
    height := 3
    // compute area and perimeter, then print the line
}
package main

import "fmt"

func main() {
    width := 7
    height := 3
    area := width * height
    perimeter := 2 * (width + height)
    fmt.Printf("Area: %d, Perimeter: %d\n", area, perimeter)
}

Next: values that never change — constants and iota.