Go Data Types: Variables, Constants & Operations
Overview
We will disucss about GO's data types, variables, constants, and arithmetic operations.
Key Points
- Go is statically typed with compile-time type checking
- Rich set of built-in types for different use cases
- Powerful variable declaration and initialization patterns
- Constants provide compile-time guarantees
- Type-safe arithmetic operations
Go's Type System
Go is a statically typed language, meaning every variable has a specific type known at compile time. This provides safety, performance, and clarity in your programs.
Type Categories
graph LR
A[Go Data Types] --> B[Basic Types]
A --> C[Composite Types]
A --> D[Reference Types]
A --> E[Interface Types]
B --> B1[Numeric]
B --> B2[String]
B --> B3[Boolean]
B1 --> B1a[Integers]
B1 --> B1b[Floating Point]
B1 --> B1c[Complex]
C --> C1[Arrays]
C --> C2[Structs]
D --> D1[Slices]
D --> D2[Maps]
D --> D3[Channels]
D --> D4[Pointers]
style A fill:#1300,stroke:#333,stroke-width:2px,color:#999
Variables
Variables are named storage locations that hold values of a specific type. Go provides several ways to declare and initialize variables.
In Golang, we use var keyword to initialize a variable. The syntax is as follows:
1 | |
Variable Declaration Methods
Declaration Patterns
| explicit_types.go | |
|---|---|
Zero Values
Variables declared without initialization get their type's zero value automatically.
| short_declaration.go | |
|---|---|
Most Common
Short declaration (:=) is the most commonly used method in Go. But please be minded that it is used when we know the value of the variable and it cannot be assigned as a global variable. If you do, you will get an error.
Multiple Variable Declaration
Declaring Multiple Variables
| grouped_vars.go | |
|---|---|
Variable Naming Conventions
Go Naming Best Practices
-
CamelCase: Use camelCase for variable names
-
Descriptive Names: Choose meaningful names
-
Short Names for Short Scope: Use short names for limited scope
-
Exported vs Unexported: Capitalization determines visibility
Remember this for the next section when we discuss about standard library.
Basic Data Types Deep Dive
Integer Types
Go provides a rich set of integer types for different use cases and memory requirements.
Integer Type Reference
| Type | Size | Range | Use Case |
|---|---|---|---|
int8 |
8 bits | -128 to 127 | Small signed values |
int16 |
16 bits | -32,768 to 32,767 | Medium signed values |
int32 |
32 bits | -2.1B to 2.1B | Large signed values |
int64 |
64 bits | -9.2E18 to 9.2E18 | Very large signed values |
uint8 |
8 bits | 0 to 255 | Small unsigned values |
uint16 |
16 bits | 0 to 65,535 | Medium unsigned values |
uint32 |
32 bits | 0 to 4.3B | Large unsigned values |
uint64 |
64 bits | 0 to 1.8E19 | Very large unsigned values |
int |
Platform | 32 or 64 bits | General purpose |
uint |
Platform | 32 or 64 bits | General unsigned |
uintptr |
Platform | Pointer size | Memory addresses |
Although, go provides us with different data types for integers. We should be very conscious about the data types we use. For example, if we use int8 and try to assign a value of 128 to it, it will give us an error. Because the maximum value that can be stored in int8 is 127. In production we might always use inferred data's to the system. So we should use int for general purpose.
Integer Examples
int can take both positive and negative numbers. So when we say var max int8, the varialbe max can hold value value from -128 to 127. So when we assign a positive value of 128 to a int8 it gives us overflow. But if we use var max uint8 we can have a number up to 0 - 255 as it can hold only positive numbers.
If you think how it's calculated, computer architecture primarily have a base 2 system (0s and 1s). So when we say we have a integer with 8 representation as bits, we can have a value of 256, that is 2^8.
Floating-Point Types
Go provides two floating-point types for decimal numbers.
Floating-Point Types
| Type | Size | Precision | Range |
|---|---|---|---|
float32 |
32 bits | ~7 decimal digits | ±1.18E-38 to ±3.4E38 |
float64 |
64 bits | ~15 decimal digits | ±2.23E-308 to ±1.8E308 |
| precision.go | |
|---|---|
String Type
Strings in Go are immutable sequences of bytes, typically containing UTF-8 encoded text.
String Operations
Boolean Type
The boolean type represents truth values: true or false.
Boolean Usage
At default the bool variable will be assigned a value false and an integer variable will be assigned a value of 0. String will be assigned a value "". As you have guessed, the complex, and float data types will be assigned to zero value based on their representation.
Key points to remember:
In Go, strings and integers are immutable. This means that once a string or integer is created, its content cannot be changed. Any operation that appears to modify a string or integer, such as concatenation or replacing characters, actually results in the creation of a new string or integer with the desired changes. The original string or integer remains unchanged in memory.
When you perform an operation that "changes" a string variable in Go, you are actually assigning a new string value to that variable name.
-
Original string creation:
-
Modification:
When you appear to modify it, a new string value is created, and the var1 pointer is updated to point to this new memory location.
The original data "apple" itself was never mutated. It is still sitting at the original memory address, untouched. The variable var1 simply stopped pointing there and started pointing somewhere else entirely.
If you had another variable pointing to the original string, it would remain unchanged:
This immutability ensures data safety, as the content that var2 sees is guaranteed not to change unexpectedly just because you reassigned var1.
Constants: Immutable Values
Constants are immutable values known at compile time. They provide guarantees and optimizations. They are hard coded during the compile time.
Constant Declaration
Constant Types
| constants.go | |
|---|---|
| typed_constants.go | |
|---|---|
Enumerations with iota
Go doesn't have built-in enums, but you can create them using constants and iota.
Using iota for Enumerations
| iota_basic.go | |
|---|---|
| iota_advanced.go | |
|---|---|
So you might have asked this question => Why does const doesn't need data type declaration like variable declaration?
The answer is Go constants don't necessarily need explicit data types because of a core language feature: "they are untyped by default until they are used in a context that requires a specific type". This design provides flexibility and prevents many common type-mismatch bugs inherent in other statically typed languages like C and C++. These languages require explicit type declarations for constants, which can lead to errors if the type is not correctly specified.
Key reasons for untyped constants:
-
Type Inference: The compiler automatically infers a default type for a constant based on its literal value (e.g., 10 is an untyped integer, "hello" is an untyped string, 3.14 is an untyped floating-point number).
-
Flexibility and Interoperability: Untyped constants can seamlessly interact with variables of various compatible types without requiring explicit type conversions, which makes the code cleaner and more intuitive.
-
Arbitrary Precision: Untyped numeric constants have theoretically arbitrary precision (or at least 256 bits of precision) during compilation. This allows for complex constant-only arithmetic with high accuracy, only losing precision when finally assigned to a fixed-size variable type.
-
Compile-Time Evaluation: All constant expressions are evaluated entirely at compile time, meaning the final values are baked into the binary, resulting in potentially better runtime performance.
Arithmetic Operations
Go provides standard arithmetic operations with type safety and predictable behavior.
Basic Arithmetic Operators
Arithmetic Operations
Note: ++counter and --counter are not valid in Go. These are not expressions but statement. Also keep in mind that we cannot assign a variable to ++counter or --counter (like b := ++counter).
And if you need to use the value after the increment/decrement, you would do it in separate steps:
Type Conversions
Go requires explicit type conversions between different numeric types. Go mandates explicit type conversions between different numeric types primarily to enhance type safety and prevent subtle bugs that can arise from implicit type coercion.
Type Conversion Examples
Advanced Type Concepts
Type Aliases and Definitions
If you have been a C/C++ developer, you might be familiar with the concept of typedef. In Go, we have similar but different concepts.
Custom Types
You might ask: Both type are float64. But why it won't work?
The answer is in Go, when you define a new type like type Temperature float64, it creates a distinct type that is not interchangeable with its underlying type (float64) without an explicit conversion. This is a safety feature to prevent accidental mixing of different semantic meanings, even if they have the same underlying representation.
Go does not allow implicit (automatic) conversion between named types and their underlying types. Even though Temperature is based on float64, Go treats Temperature as a separate type for clarity and safety.
Tip:
To verify the data type of a variable we can use the following method, %T.
Best Practices
Data Type Best Practices
-
Choose Appropriate Types
- Use
intfor general integer operations - Use specific sizes (
int32,int64) when needed, not everywhere - Prefer
float64overfloat32for precision
- Use
-
Variable Naming
- Use descriptive names for clarity
- Follow Go naming conventions
- Use short names for limited scope
-
Constants Over Variables
- Use constants for values that don't change
- Group related constants together
- Use
iotafor enumerations
-
Type Safety
- Explicit type conversions prevent bugs
- Be careful with numeric overflow
- Validate conversions when necessary
Common Pitfalls
- Integer Overflow: Be aware of type limits
- Float Precision: Floating-point arithmetic isn't exact
- Type Mixing: Go doesn't allow implicit conversions
- String Immutability: Strings can't be modified in place
Quick Reference
Key Takeaways
- Variables: Multiple declaration methods (
:=most common) - Types: Rich set of numeric, string, and boolean types
- Constants: Immutable values with compile-time guarantees
- Operations: Type-safe arithmetic with explicit conversions
- Best Practices: Choose appropriate types and follow conventions
Remember
"Go's type system is designed for clarity and safety. Embrace explicit conversions and descriptive naming for maintainable code."