Variables & Types
Python variables are names pointing to objects — no type declarations. Go variables have a fixed type at compile time. The type can be inferred from the right-hand side via :=, but once set, it never changes.
Declaration
x = 42
name = "Alice"
ratio = 3.14
active = True
items = [1, "two", 3.0] # mixed types
x := 42 // inferred int
name := "Alice" // inferred string
ratio := 3.14 // inferred float64
active := true // inferred bool
// items := []??? // no mixed-type slices
// Explicit form
var x int = 42
var name string = "Alice"
Zero Values
In Python, uninitialized variables don't exist — you get a NameError. In Go, every type has a zero value: the value a variable holds if you declare it without initialization.
| Type | Zero Value | Python Equivalent |
|---|---|---|
int, float64 | 0 | N/A (NameError) |
string | "" | N/A |
bool | false | N/A |
*T (pointer) | nil | None |
[]T (slice) | nil | None (not []) |
map[K]V | nil | None (not {}) |
struct{} | All fields zero | N/A |
Type System
// Primitive types
var i int // platform-dependent size (usually 64-bit)
var i32 int32 // explicit 32-bit
var f float64 // Python's float
var s string // immutable UTF-8 byte sequence
var b bool
var by byte // alias for uint8
var r rune // alias for int32 (Unicode code point)
// Type conversion (no implicit casting, ever)
var x int = 42
var f float64 = float64(x) // explicit
var s string = strconv.Itoa(x) // int → string (not just string(x))
string(42) produces "*" — the Unicode character at code point 42. To convert an integer to its string representation, use strconv.Itoa(42) or fmt.Sprintf("%d", 42). This trips up every Python developer exactly once.
Functions
Go functions are statically typed, can return multiple values, and are first-class (can be assigned to variables, passed as arguments). There are no default arguments, no keyword arguments, no *args/**kwargs.
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("division by zero")
return a / b
# Default args, kwargs
def greet(name, greeting="Hello"):
return f"{greeting}, {name}"
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// No default args — use options pattern
func greet(name string) string {
return "Hello, " + name
}
Multiple Return Values
Go's most distinctive feature. Functions routinely return (result, error). The caller must handle both. Python achieves this with exceptions; Go makes it explicit.
func parseConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config: %w", err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parsing config: %w", err)
}
return &cfg, nil
}
// Caller
cfg, err := parseConfig("config.json")
if err != nil {
log.Fatal(err)
}
Closures & Function Values
// Functions are values — same as Python
func makeMultiplier(factor int) func(int) int {
return func(x int) int {
return x * factor // captures factor
}
}
double := makeMultiplier(2)
fmt.Println(double(5)) // 10
Variadic Functions
// Go's version of *args — typed, not arbitrary
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
sum(1, 2, 3) // 6
sum(mySlice...) // spread a slice
Control Flow
Go's control flow is Python's with braces, no colons, mandatory braces, and no parentheses around conditions. The big addition: switch is far more powerful than Python's match and breaks by default (no fallthrough).
If / Else
if x > 10:
do_something()
elif x > 5:
do_other()
else:
fallback()
if x > 10 {
doSomething()
} else if x > 5 {
doOther()
} else {
fallback()
}
If with Init Statement
Go's killer feature for control flow — scope a variable to the if block:
// err only exists inside this if/else block
if err := doThing(); err != nil {
return err
}
// err is not accessible here
For Loops
Go has one loop keyword: for. It replaces Python's for, while, and while True.
# Iterate
for item in items:
process(item)
# With index
for i, item in enumerate(items):
process(i, item)
# While
while condition:
do_work()
# Infinite
while True:
tick()
// Iterate
for _, item := range items {
process(item)
}
// With index
for i, item := range items {
process(i, item)
}
// While
for condition {
doWork()
}
// Infinite
for {
tick()
}
Switch
// No break needed — Go breaks automatically
switch status {
case "active":
activate()
case "pending", "review": // multiple values
hold()
default:
reject()
}
// Type switch — Go's pattern matching for types
switch v := val.(type) {
case string:
fmt.Println("string:", v)
case int:
fmt.Println("int:", v)
case nil:
fmt.Println("nil")
}