Go: Go Variables and Data Types
Last updated: 2026-08-26
Variables are the cornerstone of a program—Go uses minimal syntax to make متغير declarations both clear and safe.
The design philosophy behind Go's type system: Explicit over Implicit. This means that every متغير has a well-defined type, allowing the compiler to catch more than 90% of errors at compile time. In this lesson, you'll learn all the syntax for declaring variables in Go.
1. You will learn
- The two ways to declare variables:
varand:= - The 14 Basic Data Types in Go
- Go's unique "zero value" mechanism
constconstant declarations andiotaenums- Explicit type conversion (Go does not allow implicit conversions)
fmt.Printf/fmt.Sprintfformatted output- Comprehensive Examples of User Registration Forms
2. The True Story of a Full-Stack Engineer
(1) Pain Point: Late-night debugging caused by dynamic typing
Alice is an engineer who switched from Python to Go. She recently joined the company and took over a user registration module:
"The PM said, 'After the launch, the user data was all over the place. Some people entered January 1, 2024, as their date of birth, some entered "yesterday," and others entered
فارغ—half the data in the قاعدة بيانات couldn't be parsed.'"
She took a look at the code and discovered that Python's dynamic typing was the culprit:
# Python: variable types can change at any time
birthday = "1990-01-01"
birthday = 1990 # integer suddenly valid
birthday = None # None also valid
birthday = ["today"] # list also valid!
The same variable can be a string, an integer, None, or a list in different parts of the code—there is no mechanism to prevent this confusion.
(2) Solution in Go
Alice rewrote the user model in Go:
// user.go
package main
import "fmt"
type User struct {
Name string // string
Age int // integer
Height float64 // float
IsActive bool // boolean
Birthday string // ISO 8601 date string
}
func main() {
user := User{
Name: "Alice",
Age: 28,
Height: 165.5,
IsActive: true,
Birthday: "1996-05-12",
}
// Want to change `Age` to a string? You'll get a compile-time error!
// user.Age = "twenty-eight" // ❌ cannot use "twenty-eight" as int
fmt.Printf("User: %s, Age: %d, Height: %.1fcm\n",
user.Name, user.Age, user.Height)
}
Output:
User: Alice, Age: 28, Height: 165.5cm
(3) Benefits: Captures 90% of errors during compilation
| Dimension | Python (dynamically typed) | Go (statically and strongly typed) |
|---|---|---|
| Compile-time type checking | ❌ None | ✅ Strict |
| Runtime Type Error | Frequent | Rare |
| IDE Auto-Complete | Weak | Strong |
| Refactoring Safety | Low | High |
| Null Value Trap (None/null) | Common | Zero-Value Mechanism Protection |
3. Variable Declaration: var and :=
Go provides two ways to declare variables: the var keyword (standard) and the := short declaration (shorthand).
(1) var keyword declaration (standard method)
var can be used within functions or at the package level, and there are three ways to write it:
// Method 1: Declare a variable with an explicit type
var name string = "Alice"
// Method 2: Declare a variable and omit the type (the type is inferred from the value)
var name = "Alice"
// Method 3: Declare only; initialize to zero value
var name string // name = ""
▶ Example: Comparison of three ways to write var
package main
import "fmt"
func main() {
// Method 1: Explicit Type + Initialization
var userName string = "Alice"
var userAge int = 28
// Method 2: Type Inference
var userEmail = "alice@example.com"
// Method 3: Declare only; rely on the default value
var userBio string // ""
fmt.Println(userName, userAge, userEmail, userBio)
}
Output:
Alice 28 alice@example.com
(3) := Short declaration (most common)
:= is syntactic sugar in Go; it can only be used inside functions and automatically infers the type:
package main
import "fmt"
func main() {
// Short declaration: variable_name := value
name := "Alice" // inferred as string
age := 28 // inferred as int
height := 165.5 // inferred as float64
isActive := true // inferred as bool
fmt.Printf("%s is %d years old, height %.1f, active: %v\n",
name, age, height, isActive)
}
Output:
Alice is 28 years old, height 165.5, active: true
(4) var vs :=—which one should I choose?
| Scenario | Recommended Approach | Reason |
|---|---|---|
| Package-level variables | var |
:= Cannot be used outside of functions |
| Local variables within a function | := |
Concise; saves 4 characters |
| Must be zero value | var x int |
:= Must be initialized at the same time |
| Requires an explicit type cast | var x int = int(3.14) |
The type cast is clearly visible |
| Batch declarations | var (...) |
:= does not support batch declarations |
4. Go's Basic Data Types
Go has 14 basic data types, which are divided into four major categories based on their purpose.
(1) Integer Types
graph TB
A[Integer Types] --> B[Signed int]
A --> C[Unsigned uint]
A --> D[Byte type]
A --> E[Special rune]
B --> B1[int<br/>at least 32 bits]
B --> B2[int8 / int16 / int32 / int64]
C --> C1[uint<br/>at least 32 bits]
C --> C2[uint8 / uint16 / uint32 / uint64]
D --> D1[byte = uint8<br/>emphasizes byte]
E --> E1[rune = int32<br/>emphasizes Unicode code point]
| Type | Size | Range | Typical Applications |
|---|---|---|---|
int |
32- or 64-bit (platform-dependent) | -2^31 to 2^31-1 or larger | Default integer type |
int8 |
8 bits | -128 to 127 | Small-range integers |
int32 |
32-bit | -2^31 to 2^31-1 | Cross-platform fixed size |
int64 |
64-bit | -2^63 to 2^63-1 | Large integers, timestamps |
uint |
32- or 64-bit | 0 to 2^32-1 or greater | Unsigned by default |
byte |
8 bits | 0–255 | byte, ASCII character |
rune |
32-bit | Unicode code point | Single Unicode character |
▶ Example: The difference between int and int64
package main
import (
"fmt"
"unsafe"
)
func main() {
var a int = 100
var b int64 = 200
fmt.Printf("a (int) size: %d bytes\n", unsafe.Sizeof(a))
fmt.Printf("b (int64) size: %d bytes\n", unsafe.Sizeof(b))
// You cannot directly add `int` and `int64`!
// sum := a + b // ❌ mismatched types int and int64
// An explicit conversion is required
sum := a + int(b)
fmt.Println("sum:", sum)
}
Output (on 64-bit platforms):
a (int) size: 8 bytes
b (int64) size: 8 bytes
sum: 300
(3) Floating-point types
| Type | Size | Precision | Typical Applications |
|---|---|---|---|
float32 |
32 bits | ~7 significant digits | Scientific computing, graphics |
float64 |
64-bit | ~15 significant digits | Default floating-point type |
(4) Boolean Type
var isActive bool = true
var hasPermission bool // zero value = false
(5) String Type
var greeting string = "Hello, Go!"
var emptyString string // zero value = "" (empty string)
var multiLine string = `Line 1
Line 2
Line 3` // backticks support multi-line strings
(6) Quick Reference Chart for 14 Types
| Category | Type | Zero Value |
|---|---|---|
| Integer | int / int8 / int16 / int32 / int64 |
0 |
| Unsigned integers | uint / uint8 / uint16 / uint32 / uint64 |
0 |
| Byte/Character | byte (=uint8) / rune (=int32) |
0 |
| Floating-point | float32 / float64 |
0.0 |
| Boolean | bool |
false |
| String | string |
"" |
5. Go's Unique Zero-Value Mechanism
There are no "uninitialized" variables in Go—every variable has a reasonable default value when it is declared.
▶ Example: Zero values of all types
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
var p *int // pointer zero value is nil
fmt.Printf("int: %d\n", i)
fmt.Printf("float64: %f\n", f)
fmt.Printf("bool: %v\n", b)
fmt.Printf("string: %q\n", s) // %q shows quoted string, empty string shows ""
fmt.Printf("pointer: %v\n", p)
}
Output:
int: 0
float64: 0.000000
bool: false
string: ""
pointer: <nil>
(2) Zero Values vs. Other Languages
| Type | Go zero value | Java | Python | C/C++ |
|---|---|---|---|---|
| Integer | 0 | 0 | Does not exist | Undefined (garbage value) |
| Floating-point | 0.0 | 0.0 | Does not exist | Undefined |
| Boolean | false | false | False | Undefined |
| String | "" | null | "" | Undefined |
| Pointer | nil | null | None | NULL (but prone to wild pointers) |
6. const Constants and iota Enumerations
(1) Use const to declare constants
Constants are determined at compile time, cannot be modified, and must be basic types:
const Pi = 3.14159
const AppName = "web-tutorial"
const MaxConnections = 1000
(2) Iota Enumeration: Go's Elegant Enumeration
iota is a constant counter in Go that starts at 0 and automatically increments by 1 on each line:
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Printf("Sunday=%d Monday=%d Saturday=%d\n", Sunday, Monday, Saturday)
}
Output:
Sunday=0 Monday=1 Saturday=6
▶ Example: Custom iota increment (HTTP status codes)
package main
import "fmt"
const (
StatusOK = iota * 100 // 0
StatusRedirect // 100
StatusClientError // 200
StatusServerError // 300
)
func main() {
fmt.Printf("OK=%d Redirect=%d ClientError=%d ServerError=%d\n",
StatusOK, StatusRedirect, StatusClientError, StatusServerError)
}
Output:
OK=0 Redirect=100 ClientError=200 ServerError=300
(4) Advanced iota Usage: Bitmasks (File Permissions)
package main
import "fmt"
const (
ReadPermission = 1 << iota // 1 << 0 = 1
WritePermission // 1 << 1 = 2
ExecutePermission // 1 << 2 = 4
)
func main() {
// Permission Combination: Read + Write = 1 | 2 = 3
rw := ReadPermission | WritePermission
fmt.Printf("Read=%d Write=%d Execute=%d RW=%d\n",
ReadPermission, WritePermission, ExecutePermission, rw)
}
Output:
Read=1 Write=2 Execute=4 RW=3
iota is the only syntax in Go that comes close to an "enum," but it isn't as strict as the enum types in Python or Java. If you need stronger type safety in a production environment, you can define your own types.
7. Explicit Type Conversion
Go does not allow implicit type conversions—all conversions must be explicitly declared. This reflects Go's "explicit over implicit" principle.
(1) Numeric Type Conversion
package main
import "fmt"
func main() {
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
fmt.Printf("i=%d f=%v u=%d\n", i, f, u)
// Truncation example: Converting a float to an int results in the loss of the decimal part
var pi float64 = 3.14159
var truncated = int(pi)
fmt.Printf("pi=%.5f truncated=%d\n", pi, truncated)
}
Output:
i=42 f=42 u=42
pi=3.14159 truncated=3
(2) String conversion: the strconv package
package main
import (
"fmt"
"strconv"
)
func main() {
// String → Number
n, err := strconv.Atoi("123")
fmt.Printf("n=%d err=%v\n", n, err)
// Number → String
s := strconv.Itoa(456)
fmt.Printf("s=%s (type: %T)\n", s, s)
// String → float
f, _ := strconv.ParseFloat("3.14", 64)
fmt.Printf("f=%f\n", f)
// float → string (specified precision)
fmt.Println(strconv.FormatFloat(3.14159, 'f', 2, 64)) // "3.14"
}
Output:
n=123 err=<nil>
s=456 (type: string)
f=3.140000
3.14
(3) Implicit Conversion vs. Explicit Conversion
| Scenario | Java/C++ | Go |
|---|---|---|
int + float |
Automatically converted to float | Compilation error; must be explicit |
int32 + int64 |
Automatically converted to int64 | Compilation error |
int to string |
Implicit | Requires strconv.Itoa() |
8. fmt: Format Output
(1) Common formatting verbs
| Verb | Meaning | Applicable Types |
|---|---|---|
%d |
Decimal integer | int, int8/16/32/64 |
%f |
Floating-point number | float32, float64 |
%s |
string | string |
%v |
Default format for any type | Any type |
%T |
Type | Any type |
%t |
Boolean | bool |
%q |
Quoted string | string, rune |
%x |
Hexadecimal | int, []byte |
%p |
Pointer Address | pointer |
%% |
Literal percent sign | — |
▶ Example: Full-featured demonstration of fmt.Printf
package main
import "fmt"
func main() {
name := "Alice"
age := 28
height := 165.5
isActive := true
hobbies := []string{"coding", "reading", "hiking"}
fmt.Printf("Name: %s\n", name) // %s string
fmt.Printf("Age: %d\n", age) // %d integer
fmt.Printf("Height: %.1f cm\n", height) // %.1f float with 1 decimal
fmt.Printf("Active: %t\n", isActive) // %t boolean
fmt.Printf("Hobbies: %v\n", hobbies) // %v default format
fmt.Printf("Name type: %T\n", name) // %T type
fmt.Printf("100%% complete!\n") // %% literal %
// %v Advanced usage: %+v displays the field name (struct), %#v displays Go syntax
fmt.Printf("hobbies: %#v\n", hobbies)
}
Output:
Name: Alice
Age: 28
Height: 165.5 cm
Active: true
Hobbies: [coding reading hiking]
Name type: string
100% complete!
hobbies: []string{"coding", "reading", "hiking"}
(3) The Difference Between Print, Println, and Printf
| Function | Output Termination | Use Cases |
|---|---|---|
fmt.Print() |
No line break | Continuous output |
fmt.Println() |
Automatic line breaks + space-separated | Simple debugging |
fmt.Printf() |
Does not add a newline; requires \n |
Formatted output |
fmt.Sprintf() |
Returns a string; does not print | Concatenates strings |
9. Complete Example: User Registration Form
Pull all the key concepts from this lesson together and write a user registration data collector:
// main.go
package main
import (
"fmt"
"strconv"
)
// Defining User Role Enumerations Using Iota
const (
RoleGuest = iota // 0 Guest
RoleUser // 1 Regular User
RoleAdmin // 2 Admin
RoleSuperAdmin // 3 Super Admin
)
func main() {
// 1. Use the := short declaration for user information
name := "Alice"
age := 28
height := 165.5
isActive := true
role := RoleUser
// 2. Explicit Type Conversion
ageAsFloat := float64(age)
heightAsString := strconv.FormatFloat(height, 'f', 1, 64)
// 3. const Constants
const MaxLoginAttempts = 5
// 4. fmt: Formatted Output
fmt.Println("========================================")
fmt.Println(" User Registration Form")
fmt.Println("========================================")
fmt.Printf("Name: %s\n", name)
fmt.Printf("Age: %d (%.0f years old)\n", age, ageAsFloat)
fmt.Printf("Height: %s cm\n", heightAsString)
fmt.Printf("Active: %t\n", isActive)
fmt.Printf("Role: %d (User)\n", role)
fmt.Printf("Max Attempts: %d\n", MaxLoginAttempts)
fmt.Printf("Type Check: name is %T, age is %T\n", name, age)
fmt.Println("========================================")
// 5. Demonstration of Zero Values for Variables
var uninitializedAge int
fmt.Printf("\nUninitialized age (zero value): %d\n", uninitializedAge)
}
Expected Output:
========================================
User Registration Form
========================================
Name: Alice
Age: 28 (28 years old)
Height: 165.5 cm
Active: true
Role: 1 (User)
Max Attempts: 5
Type Check: name is string, age is int
========================================
Uninitialized age (zero value): 0
strconv.Atoi("abc") will return an error, which must not be ignored. Correct syntax: n, err := strconv.Atoi("abc"); if err != nil { ... }. Lesson 8 delves deeper into error handling.
❓ FAQ
var or :=?:= inside functions—it's concise and enforces initialization; package-level variables must use var. The bytecode generated by both is exactly the same; it's just syntactic sugar.int varies between 32-bit and 64-bit platforms. What issues might this cause?int32 or int64 to avoid cross-platform compatibility issues.enum keyword. How do you create an enumeration?const + iota. Although it's not as strict as Java's enum class, it's sufficient. If you need type safety, you can use a custom type like type Role int along with a set of constants.iota starts at 0. Can it start at 1?_ = iota to skip 0, or iota + 1: const (A = iota + 1; B; C) results in A=1, B=2, C=3. This is commonly used in scenarios where values need to start at 1, such as HTTP status codes and months.int and long operations). Go's strict type conversion makes code more readable and results in fewer bugs.string and []byte?[]byte(s) converts a string to a byte slice (copying the data); string(b) converts a byte slice to a string. These are commonly used in network I/O and encryption scenarios. Note: []byte is suitable for modification, while string is suitable for read-only use.&Pi will result in a compile-time error.📖 Summary
- In Go, variables can be declared using either
varor:=. The use of:=is recommended within functions, whilevaris required at the package level. - 14 basic types divided into 4 categories: integers (int/int8–64), floating-point numbers (float32/64), booleans, and strings
- The zero-value mechanism is a safety feature in Go: every variable has a reasonable default value, which eliminates wild pointers.
constdeclares constants;iotaprovides an elegant way to enumerate (HTTP status codes, bit masks)- Go prohibits implicit type conversions; they must be explicit (
int(x)/strconv.Itoa()) fmt.Printfuses format specifiers (%d,%f,%s,%v,%T,%q,%t) to control the output- Go's static, strongly typed language catches 90% of errors at compile time, making it the cornerstone of collaborative software development
📝 Exercises
-
Basic Exercise (Difficulty ⭐): Declare 5 variables (name/age/height/isStudent/gpa) using both the
varand:=syntaxes, and print their values and types. -
Advanced Exercise (Difficulty ⭐⭐): Use
iotato define four HTTP status code constants (200/404/500/503), and write ahandleRequest(code int)function that returns the corresponding text (e.g., "OK," "Not Found," "Internal Server Error," or "Service Unavailable") based on the status code. -
Challenge Problem (Difficulty ⭐⭐⭐): Write a unit converter: Take a temperature in Fahrenheit as input and output the temperature in Celsius (formula: C = (F - 32) × 5/9). Requirements: (1) Use
fmt.Scanfto read the input; (2) Usestrconv.ParseFloatfor type conversion; (3) Handle invalid input (return an error).