We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

Variables in Go

Lane Wagner
Lane WagnerBoot.dev co-founder and backend engineer

Last published

Table of Contents

Variables in Go have a type that is known before the code runs. Here's how declarations, type inference, conversions, and scope work.

All the content from our Boot.dev courses are available for free here on the blog. This one is the "Variables" chapter of Learn Go. If you want to try the far more immersive version of the course, do check it out!

Declaring a Variable

The var keyword is used to declare a variable the sad way. For example, to declare an integer variable called mySkillIssues and assign it the value 42:

// create a new variable, it defaults to 0
// (the "zero value" for ints)
var mySkillIssues int

// overwrite the zero value with 42
mySkillIssues = 42

Basic Variables

Some of Go's most common variable types are:

  • int: a signed integer
  • bool: a boolean value, either true or false
  • string: a sequence of characters
  • float64: a floating-point number
  • byte: exactly what it sounds like: 8 bits of data

And to use them, their values are set like this:

var health int
health = 100

var isAwesome bool
isAwesome = true

var greeting string
greeting = "Hello, world!"

var pi float64
pi = 3.14159

var data byte
data = 0xFF

Short Variable Declaration

Sad variable declaration:

var mySkillIssues int
mySkillIssues = 42

GOATed variable declaration:

mySkillIssues := 42

The walrus operator, :=, declares a new variable and assigns a value to it in one line. Go can infer that mySkillIssues is an int because of the 42 value. Yay type inference!

When to Use the Walrus Operator

The :=, (walrus operator) should be used instead of var style declarations basically anywhere possible. The limitation is that := can't be used outside of a function (in the global/package scope).

Type inference is based on the value being assigned.

An int:

mySkillIssues := 42

A float64:

pi := 3.14159

A string:

message := "Hello, world!"

A bool:

isGoat := true

Same Line Declarations

You can declare multiple variables on the same line:

mileage, company := 80276, "Toyota"

The above is the same as:

mileage := 80276
company := "Toyota"

Type Sizes

Integers, uints, floats, and complex numbers all have type sizes.

  • Signed integers (no decimal)
int  int8  int16  int32  int64
  • Unsigned integers (non-negative numbers/no decimal)
uint uint8 uint16 uint32 uint64 uintptr
  • Signed decimal numbers
float32 float64
  • Complex numbers (a complex number has a real and imaginary part)
complex64 complex128

What's the Deal With the Sizes?

The size (8, 16, 32, 64, 128, etc.) represents how many bits in memory will be used to store the variable. The "default" int and uint types refer to their respective 32 or 64-bit sizes depending on the environment of the user.

The "standard" types that should be used unless you have a specific performance need (e.g. using less memory) are:

  • int
  • uint
  • float64
  • complex128

Converting Between Types

Some types can be easily converted like this:

temperatureFloat := 88.26
temperatureInt := int(temperatureFloat)

Casting a float to an integer in this way truncates the floating point portion.

Which Type Should I Use?

With so many types for what is essentially just a number, developers coming from languages that only have one kind of Number type (like JavaScript) may find the choices daunting.

Prefer “Default” Types

A problem arises when we have a uint16, and the function we are trying to pass it into takes an int. We're forced to write code riddled with type conversions like:

var myAge uint16 = 25
myAgeInt := int(myAge)

This style of code can be slow and annoying to read. When Go developers stray from the "default" type for any given type family, the code can get messy quickly. Unless you have a good performance related reason, you'll typically just want to use the "default" types:

  • bool
  • string
  • int
  • uint
  • byte
  • rune
  • float64
  • complex128

When Should I Use a More Specific Type?

When you're super concerned about performance and memory usage.

That's about it. The only reason to deviate from the defaults is to squeeze out every last bit of performance when you are writing an application that is resource-constrained. (Or, in the special case of uint64, you need an absurd range of unsigned integers).

Go Is Statically Typed

Go enforces static typing meaning variable types are known before the code runs. That means your editor and the compiler can display type errors before the code is ever run, making development easier and faster.

Contrast this with most dynamically typed languages like JavaScript and Python... Dynamic typing often leads to subtle bugs that are hard to detect. The code must be run to catch syntax and type errors. (sometimes in production if you're unlucky 😨)

Languages also have strong or weak typing, meaning stricter or weaker type checking rules.

Concatenating Strings

Two strings can be concatenated with the + operator. But the compiler will not allow you to concatenate a string variable with an int or a float64.

Block Scope

Unlike Python, Go is not function-scoped, it's block-scoped. Variables declared inside a block are only accessible within that block (and its nested blocks). There's also the package scope. You can think of it as the outermost, nearly global scope.

package main

// scoped to the entire "main" package (basically global)
var age = 19

func sendEmail() {
    // scoped to the "sendEmail" function
    name := "Jon Snow"

    for i := 0; i < 5; i++ {
        // scoped to the "for" body
        email := "[email protected]"
    }
}

Blocks are defined by curly braces {}. New blocks are created for:

  • Functions
  • Loops
  • If statements
  • Switch statements
  • Select statements
  • Explicit blocks

It's a bit unusual, but occasionally you'll see a plain old explicit block. It exists for no other reason than to create a new scope.

package main

import "fmt"

func main() {
    {
        age := 19
        // this is okay
        fmt.Println(age)
    }

    // this is not okay
    // the age variable is out of scope
    fmt.Println(age)
}

Frequently Asked Questions

How do you declare a variable in Go?

Use the var keyword, or use := inside a function to declare a new variable and assign a value to it in one line.

Can you use := outside a function in Go?

No. The := short declaration syntax cannot be used outside of a function in the global or package scope.

How does Go infer a variable's type?

Type inference is based on the value being assigned. For example, Go infers int from 42 and float64 from 3.14159.

What does static typing mean in Go?

Variable types are known before the code runs, so editors and the compiler can display type errors before the code is run.

What is block scope in Go?

Variables declared inside a block are only accessible within that block and its nested blocks.