In the previous lesson, we explored how to store data using variables with var and :=. We saw that Go is statically typed, meaning every variable's type is fixed when the program is compiled. This lesson builds directly on that foundation. We will formally cover Go's basic data types—bool, int, float64, and string—which are the fundamental building blocks for all data in your programs.
Furthermore, we'll introduce constants, which are immutable values that, once declared, cannot be changed. Understanding both variables and constants, along with their types, is essential for correctly representing and manipulating data—a core task for any backend developer.
1. The Core Building Blocks: Basic Data Types
In the last lesson, you used types like string and int without much formal introduction. Let's now take a systematic look at the most common basic types that you will use in virtually every Go program.
The article "Basic Data Types in Go" from golangbot.com provides an excellent overview. We will use it as a reference as we go through each type.
Basic Data Types in Go | golangbot.com
This article provides a clear, example-driven tour of Go's basic data types.
Read through the sections on bool, Signed integers, Unsigned integers, Floating point types, and the string type. As you read, focus on: The possible values for each type (e.g., true/false for bool). The distinction between int (general-purpose) and sized integers like int64. The default type for floating-point numbers (float64). How to concatenate strings using the + operator. You can skip the sections on Complex types for now. We will also cover byte and rune in more detail in a future lesson on strings. Start by reading the section on the bool type. Then, review the tables and examples for signed integers and unsigned integers. Continue with the section on floating point types, and finally, read the introduction to the string type.
To summarize and expand on what you've just read:
bool: The simplest type, with only two possible values:trueandfalse. It's the foundation of all conditional logic.- Numeric Types:
- Integers: Go provides a variety of integer types. For general use, you'll almost always use
int. This type is the most efficient for your computer's architecture (either 32 or 64 bits). You'd only use specific sizes likeint8,int16,int32, orint64when you need to control memory usage precisely or interface with external systems that have specific data requirements.uintand its variants are for non-negative numbers only. - Floating-Point: For numbers with decimal points, you have
float32andfloat64. As the article notes, if you writex := 3.14, Go infers the type asfloat64. You should default to usingfloat64unless you have a specific reason to conserve memory.
- Integers: Go provides a variety of integer types. For general use, you'll almost always use
string: This type holds textual data, like"Hello, World!". As mentioned, strings are fundamentally sequences of bytes. We can join them together using the+operator.
A Note on Type Conversion
Coming from Python, where you can often mix numeric types (like adding an integer to a float) without issue, Go's strictness can be surprising. Go does not allow operations between variables of different types without an explicit conversion.
This is a key safety feature. It forces you, the programmer, to be explicit about your intentions, which prevents a class of bugs that can arise from unexpected type promotions.
Let's revisit the golangbot.com article to see this in action.
Basic Data Types in Go | golangbot.com
This section demonstrates Go's strict typing rules.
Read the section Type Conversion. Pay close attention to the code example where adding an int and a float64 causes a compilation error, and how it is fixed using the T(v) syntax (e.g., int(b)).
The syntax T(v) converts the value v to the type T. For example, float64(myInt) converts the integer myInt to a float64. You must perform these conversions yourself whenever you mix numeric types in an expression.
2. Immutable Values: Constants
While variables give us named storage for values that can change, sometimes we need to name values that must not change. These are called constants. Examples include mathematical constants like Pi, configuration settings like a default port number, or specific error codes.
Constants are declared with the const keyword. By convention, their names are written in CamelCase, just like variables.
Variables and Constants In Go - Learn Golang #2
This video by John Elder provides a concise introduction to constants.
Watch the segment from the introduction to constants, where the presenter declares a constant and shows the compile error that occurs when you try to reassign it. Then, continue watching to see how to declare a block of constants using parentheses.
As the video shows, the fundamental rule of constants is that they are immutable. The compiler will prevent you from ever changing their value after declaration.
Typed vs. Untyped Constants
Here we arrive at a powerful and unique feature of Go. Constants can be either typed or untyped.
- A typed constant has a fixed type, just like a variable.
const MyInt int32 = 100 - An untyped constant is a high-precision value that doesn't have a fixed type until it's used in a context that requires one.
const UntypedInt = 100
This flexibility is incredibly useful. An untyped integer constant can be assigned to a variable of type int, int32, int64, or even float64, as long as the value fits. A typed constant can only be assigned to a variable of its exact type.
The following article explains this distinction clearly.
How to Understand Constant Types and Untyped Constants in Go
This article from OneUptime provides an excellent explanation of the difference between typed and untyped constants.
First, read the section Typed vs Untyped Constants. Focus on the example code that shows how untypedInt can be assigned to different variable types, whereas typedInt cannot. Then, read the section on Numeric Conversions, which reinforces this same concept from a different angle.
In general, you should prefer untyped constants unless you have a specific reason to lock a constant to a particular type.
Enumerated Constants with iota
Go has a special keyword, iota, that simplifies the declaration of sequential constants, often called enumerations (or enums). iota is a number that starts at 0 within a const block and increments by one for each subsequent constant declaration.
How to Understand Constant Types and Untyped Constants in Go
This part of the article introduces iota and its common patterns.
Start with the section The iota Identifier to see its basic usage. Then, review the more advanced iota Patterns, especially the example for KB, MB, GB, which is a very common Go idiom for creating powers of two.
iota is a powerful tool for creating clear, readable sets of related constants without having to manually assign each value.
Practice
Time to put these concepts into practice. Create a new file named types.go and add the following code. Follow the instructions in the comments to complete the exercise.
package main
import "fmt"
// 1. Define a block of enumerated constants for the primary colors
// using iota. The first color should be Red = 0.
const (
Red = iota // Should be 0
Green // Should be 1
Blue // Should be 2
)
// 2. Define an untyped constant for the server port.
const Port = 8080
func main() {
// 3. Declare variables for the following, using the most appropriate basic type:
// - A boolean for whether the service is active.
// - A string for the service name.
// - A float for the current CPU usage (e.g., 15.5).
isActive := true
serviceName := "AuthService"
cpuUsage := 15.5 // Go will infer this as float64
fmt.Printf("Service '%s' is active: %v\n", serviceName, isActive)
fmt.Printf("Current CPU: %.1f%%\n", cpuUsage)
fmt.Printf("Default Port: %d\n", Port)
// 4. We need to calculate the total port number by adding an integer
// offset to our float64 Port constant. This will fail initially.
// Fix the code by explicitly converting one of the values so the types match.
offset := 10
// totalPort := Port + offset // This would work because Port is untyped
// Let's create a harder case with a typed float
var typedPort float64 = 8080.0
// This line will cause a compiler error: invalid operation: typedPort + offset (mismatched types float64 and int)
// totalPort := typedPort + offset
// Fix it here:
totalPort := int(typedPort) + offset
fmt.Printf("Calculated Port: %d\n", totalPort)
// 5. Print the value of the Green constant you defined above.
fmt.Printf("The value of Green is: %d\n", Green)
}
Experiment with the code. See what happens if you try to assign Green a new value. Change the totalPort calculation back to the version that fails and read the compiler error carefully—it's very descriptive and helpful.
Conclusion
In this lesson, we formalized your understanding of Go's fundamental data types and introduced the concept of constants. These are the atoms and molecules of the data you will manipulate in all your future Go programs.
Key takeaways:
- Basic Types: Go's core data types are
bool,int,float64, andstring. - Static Typing: Go is strict about types. You cannot mix numeric types in operations without explicit conversion using the
T(v)syntax. - Constants: Declared with
const, they are immutable values fixed at compile time. - Untyped vs. Typed Constants: Untyped constants are flexible and adopt a type when used, while typed constants are rigid. Prefer untyped.
iota: A special identifier used to create sequences of enumerated constants easily.
Now that you can store data in variables and constants, the next step is to control your program's execution based on that data. In the next lesson, we will dive into conditional logic using if/else and switch statements, which will allow you to make your programs dynamic and responsive.