Variables in Go
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 integerbool: a boolean value, eithertrueorfalsestring: a sequence of charactersfloat64: a floating-point numberbyte: 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:
intuintfloat64complex128
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:
boolstringintuintbyterunefloat64complex128
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.
