Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

Go Variables and Constants

Recommended resources

In our previous lesson, you set up a Go project and learned to build and run a program. Now that you have the basic toolchain mechanics down, we can start writing more interesting code. This lesson moves from project structure to the data that flows through it, addressing how to declare variables and constants in Go.

As an experienced developer familiar with Python's dynamic typing and TypeScript's sophisticated static type system, you'll find Go's approach to be a unique blend of explicitness and conciseness. We'll focus on Go's static type system, its concept of "zero values," and the idiomatic short variable declaration syntax (:=) that you'll see everywhere in Go code.

Variables, Types, and Zero Values

Go is a statically typed language. Once a variable is declared to have a certain type, it cannot hold a value of a different type. This provides compile-time safety, a concept you're well-acquainted with from TypeScript. However, unlike Python where variables don't have explicit types, in Go, every variable does.

The foundational way to declare a variable is with the var keyword, which follows the pattern var <name> <type>.

A key concept in Go is the zero value. When a variable is declared without an explicit initial value, Go automatically initializes it to a "zero value" corresponding to its type. This prevents null or undefined errors for uninitialized variables, which is a common source of bugs in other languages.

  • 0 for numeric types (int, float64, etc.)
  • false for bool
  • "" (an empty string) for string
  • nil for pointers, slices, maps, interfaces, channels, and functions (we'll cover these later)

The following video provides a good overview of Go's basic types and the two main variable declaration syntaxes.

Golang Tutorial #2 - Variables & Data Types

This video from the "Tech With Tim" channel introduces Go's type system and variable declaration.

Please watch the following segments: First, watch the introduction to understand how Go's static typing compares to dynamic languages like Python at the beginning. Next, see the var keyword in action and learn about naming conventions from this segment. Finally, pay close attention to the part explaining Go's strict "declared and not used" rule, which is a distinctive feature of the language, from this part of the video.

As you saw, declaring a variable without using it results in a compilation error. This forces developers to maintain clean, readable code by removing unused variables.


The Idiomatic Way: Short Variable Declaration

While var is clear and explicit, the most common and idiomatic way to declare and initialize a variable inside a function is with the short variable declaration operator, :=.

This operator combines declaration and initialization in a single, concise statement. It infers the variable's type from the value on the right-hand side. For example:
message := "Hello, Go!"

Here, Go infers that message is of type string. This is only available within functions; declarations at the package level must use the var keyword.

The article below does an excellent job of comparing Go's syntax with that of JavaScript and Python, which should provide a familiar frame of reference.

Trying Golang as a JavaScript lover | blog | puruvj.dev

This blog post by Puru Vijay compares Go's variable syntax directly to what you're used to in Python and JavaScript/TypeScript.

Please read the section titled "Variables are Pythonic, but not quite…" to see how the := operator provides a clear distinction between declaration and assignment, a pain point the author notes in Python. Also, review the part on the more verbose var syntax.

One crucial point about := is that it is used for declaration. To change the value of an existing variable, you use the standard assignment operator, =. The compiler will throw an error if you use := on a variable that has already been declared in the same scope.

This image shows a `main.go` file. The first set of declarations on line 8 uses `:=` to create three new variables: `price`, `tax`, and `inStock`. On line 12, `:=` is used again to declare `price2`, but it re-assigns a new value to the existing `tax` variable. This is allowed because at least one variable on the left (`price2`) is new.

The image highlights a special rule: you can use := in a multi-variable assignment if at least one of the variables on the left side is new. This is frequently used in Go for handling operations that return a value and an error, like value, err := someFunction().


Constants: Simple Values with a Powerful Twist

Constants in Go are declared using the const keyword. They are simple, immutable values that are known at compile time. Their syntax is straightforward and similar to const in TypeScript.

const APIKey = "your-secret-key"

You cannot use the := syntax to declare a constant.

This image shows a common mistake: trying to use the short variable declaration operator `:=` with the `const` keyword. This results in a compilation error.

However, Go constants have a powerful underlying feature that makes them much more flexible than constants in many other languages: they can be untyped. This is a core "Go way" concept that enables clean, readable code despite the strict type system.

An untyped constant is a value that does not yet have a fixed, concrete Go type. It exists as a high-precision numerical value. Only when it's used in a context that requires a type—like assigning it to a variable—does it take on a type.

For example, const MyFloat = 3.14 is an untyped floating-point constant. It can be assigned to a float64 or a float32 variable without an explicit conversion. The compiler handles fitting the value to the required type.

The official Go blog post on this topic by Rob Pike, one of Go's creators, is the definitive resource for understanding this concept.

Constants - The Go Programming Language

This article explains the design philosophy behind Go's constants, particularly the concept of untyped constants.

This is a deeper dive, so focus on grasping the core ideas: Read the Introduction to understand the problem Go's design solves—avoiding the complex type promotion rules of languages like C. Next, read the definition of a Go constant. The most important concept is introduced in the String constants section. Pay close attention to the distinction between a typed and an untyped constant. Then, read about the Default type to understand how an untyped constant gets a type (e.g., in a := declaration). Finally, read the section Numbers. It provides a brilliant summary of how untyped numeric constants live in a "unified space," allowing you to write natural-looking code like math.Sqrt(2) even though 2 is an integer and the function expects a float64.


Let's Write Some Code

Now, let's apply these concepts. Open the main.go file from our last lesson and replace its content with the following code. It demonstrates variable and constant declaration, zero values, type inference, and the flexibility of untyped constants.

package main

import "fmt"

// A package-level constant. It is untyped.
const SecondsInHour = 3600

func main() {
    // Explicit declaration using 'var'.
    var language string = "Go"
    fmt.Printf("Language is: %s\n", language)

    // Short variable declaration using ':='. Type is inferred.
    version := 1.21
    fmt.Printf("Version is: %.2f (type is %T)\n", version, version)

    // Multiple declaration.
    isRunning, memoryUsage := true, 512
    fmt.Printf("Running: %t, Memory: %dMB (types are %T, %T)\n", isRunning, memoryUsage, isRunning, memoryUsage)
    
    // Demonstrating zero values.
    var requestCount int
    var statusMessage string
    fmt.Printf("Initial request count: %d, status: '%s'\n", requestCount, statusMessage)

    // Using an untyped constant with a typed variable.
    var minutes float32 = 15.5
    // 'SecondsInHour' (untyped int constant) can be used in an
    // expression with 'minutes' (a float32) without a type error.
    totalSeconds := minutes * 60 
    fmt.Printf("%.1f minutes is %.0f seconds.\n", minutes, totalSeconds)

    hours := 2.5
    // The untyped constant 'SecondsInHour' can also be used here with a float64.
    secondsInHours := hours * SecondsInHour
    fmt.Printf("%.1f hours is %.0f seconds. (type of result is %T)\n", hours, secondsInHours, secondsInHours)
}

Run this program using go run . in your terminal. Pay attention to the output of the %T format specifier in the Printf statements, which prints the type of the variable. Notice how the type of version is float64 (the default for floating-point literals) and how secondsInHours also becomes a float64 because it's used in an expression with hours.


Conclusion

In this lesson, you've learned the fundamentals of how Go handles data through variables and constants.

Key takeaways include:

  • Go is statically typed, providing compile-time safety.
  • Variables can be declared with var name type, but the idiomatic way inside functions is with the short declaration operator name := value, which infers the type.
  • Variables declared without a value are initialized to a zero value, not null or undefined.
  • Constants are declared with const and are compile-time values. They can be untyped, which gives them great flexibility to be used in different numeric contexts without explicit type conversions.

You now have the building blocks to represent single pieces of data. In our next lesson, we will build upon this by exploring how Go manages collections of data using arrays, slices, and maps, comparing them directly to the list, dictionary, and array structures you know from Python and TypeScript.