Variables, Constants & Data Types Beginner¶
Go is statically typed with type inference -- you get the safety of explicit types with the brevity of dynamic languages. Understanding Go's type system, declaration styles, and zero values is foundational to everything else.
Variable Declaration¶
Go offers two declaration styles. Knowing when to use each is an interview staple.
var keyword (explicit declaration)¶
var name string // zero value: ""
var age int = 30 // explicit type + value
var ratio float64 = 0.85 // explicit type + value
var active bool // zero value: false
// Block declaration
var (
host string = "localhost"
port int = 8080
verbose bool
)
Short declaration := (inside functions only)¶
func main() {
name := "Go" // type inferred as string
count := 42 // type inferred as int
rate := 3.14 // type inferred as float64
done := false // type inferred as bool
// Multiple assignment
x, y, z := 1, 2.5, "three"
}
var vs := -- When to Use Which¶
| Feature | var |
:= |
|---|---|---|
| Package level | Yes | No |
| Inside functions | Yes | Yes |
| Type inference | Optional | Always |
| Zero value init | Yes (var x int) |
No (must assign) |
| Redeclaration in multi-assign | No | Yes (at least one new) |
// := allows redeclaration when at least one variable is new
val, err := strconv.Atoi("42")
val2, err := strconv.Atoi("99") // err is redeclared, val2 is new -- OK
:= is NOT available at package level
Zero Values¶
Every type in Go has a zero value -- the default when no value is assigned. There is no undefined or null surprise.
| Type | Zero Value | Example |
|---|---|---|
bool |
false |
var b bool → false |
int, int8...int64 |
0 |
var n int → 0 |
uint, uint8...uint64 |
0 |
var u uint → 0 |
float32, float64 |
0.0 |
var f float64 → 0.0 |
complex64, complex128 |
(0+0i) |
var c complex128 → (0+0i) |
string |
"" (empty) |
var s string → "" |
byte (alias uint8) |
0 |
var b byte → 0 |
rune (alias int32) |
0 |
var r rune → 0 |
| Pointer | nil |
var p *int → nil |
| Slice | nil |
var s []int → nil |
| Map | nil |
var m map[string]int → nil |
| Channel | nil |
var ch chan int → nil |
| Interface | nil |
var i interface{} → nil |
| Function | nil |
var f func() → nil |
| Struct | All fields zeroed | var t time.Time → 0001-01-01 00:00:00 |
Interview Tip
"In Go, every variable is usable at its zero value. A sync.Mutex works without initialization, bytes.Buffer is ready to write to, and slices can be appended to even when nil. This is a deliberate design choice that eliminates an entire class of constructor-related bugs."
Basic Data Types¶
Numeric Types¶
| Type | Size | Range |
|---|---|---|
int8 |
1 byte | -128 to 127 |
int16 |
2 bytes | -32,768 to 32,767 |
int32 |
4 bytes | -2.1B to 2.1B |
int64 |
8 bytes | ±9.2 × 10¹⁸ |
int |
Platform-dependent | 32 or 64 bit |
uint8 / byte |
1 byte | 0 to 255 |
uint16 |
2 bytes | 0 to 65,535 |
uint32 |
4 bytes | 0 to 4.2B |
uint64 |
8 bytes | 0 to 18.4 × 10¹⁸ |
uint |
Platform-dependent | 32 or 64 bit |
uintptr |
Platform-dependent | Holds a pointer value |
float32 |
4 bytes | ~7 decimal digits precision |
float64 |
8 bytes | ~15 decimal digits precision |
complex64 |
8 bytes | float32 real + imag |
complex128 |
16 bytes | float64 real + imag |
var (
counter int = 1_000_000 // underscores for readability (Go 1.13+)
price float64 = 19.99
initial byte = 'A' // byte is uint8
codepoint rune = '世' // rune is int32
mask uint8 = 0xFF
bigNum int64 = 1 << 40
)
String and Boolean¶
var (
greeting string = "Hello, Go"
raw string = `Line 1\nStill line 1` // raw string literal, no escape processing
flag bool = true
)
// Strings are immutable byte slices
s := "hello"
// s[0] = 'H' // COMPILE ERROR: cannot assign to s[0]
Constants¶
Constants are compile-time values. They cannot be declared with :=.
Untyped Constants¶
Go constants can be untyped, giving them higher precision and flexibility.
const x = 1_000_000_000_000_000 // untyped -- works with any numeric type
var a int32 = x // OK if x fits in int32 range -- this won't, compile error
var b int64 = x // OK
var c float64 = x // OK
iota -- Enumeration Generator¶
iota starts at 0 and increments by 1 for each constant in a const block.
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
Common iota Patterns¶
// Skip zero value (useful to detect unset)
type Color int
const (
_ Color = iota // skip 0
Red // 1
Green // 2
Blue // 3
)
// Bit flags
type Permission uint8
const (
Read Permission = 1 << iota // 1
Write // 2
Execute // 4
)
func hasPermission(p, flag Permission) bool {
return p&flag != 0
}
// Size units
const (
_ = iota
KB = 1 << (10 * iota) // 1024
MB // 1,048,576
GB // 1,073,741,824
TB // 1,099,511,627,776
)
Interview Tip
"Skip iota value 0 when the zero value would be ambiguous. For example, if you have a Status enum, an uninitialized variable would silently become the first status. Using _ = iota for 0 forces explicit assignment."
Type Conversions¶
Go has no implicit type conversions. Every conversion must be explicit.
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
// String conversions
s := string(65) // "A" (Unicode codepoint)
n, err := strconv.Atoi("42") // string → int (with error check)
s2 := strconv.Itoa(42) // int → string
s3 := fmt.Sprintf("%d", 42) // int → string via formatting
// Between integer types -- be careful of overflow
var big int64 = 1<<40
var small int32 = int32(big) // SILENT OVERFLOW -- no runtime error
No Implicit Conversions
Type Aliases vs Custom Types¶
// Type alias -- same type, new name (Go 1.9+)
type NodeID = int64 // NodeID IS int64, fully interchangeable
// Custom type -- new distinct type
type UserID int64 // UserID is NOT int64, needs conversion
var n NodeID = 42
var i int64 = n // OK -- alias is the same type
var u UserID = 42
// var j int64 = u // COMPILE ERROR -- different types
var j int64 = int64(u) // explicit conversion required
Custom types can have methods attached; aliases cannot.
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
temp := Celsius(100)
fmt.Println(temp.ToFahrenheit()) // 212
Quick Reference¶
| Operation | Syntax | Notes |
|---|---|---|
| Declare with zero value | var x int |
Package or function level |
| Declare with value | var x int = 5 |
Explicit type |
| Short declare | x := 5 |
Function level only, type inferred |
| Multiple declare | var a, b int |
Same type |
| Multiple short | a, b := 1, "hi" |
Different types OK |
| Constant | const x = 5 |
Compile-time, untyped by default |
| Typed constant | const x int = 5 |
Fixed type |
| Enum with iota | const ( A = iota; B; C ) |
0, 1, 2 |
| Type conversion | float64(x) |
Always explicit |
| Custom type | type Age int |
Distinct type, can have methods |
| Type alias | type ID = int64 |
Same type, new name |
Best Practices¶
- Prefer
:=inside functions -- it's idiomatic and concise. - Use
varfor zero-value initialization --var buf bytes.Bufferis clearer thanbuf := bytes.Buffer{}. - Use
varat package level --:=isn't allowed there anyway. - Group related
varandconstdeclarations in blocks for readability. - Prefer
intunless you need a specific size -- it matches the platform word size and avoids unnecessary conversions. - Use custom types for domain concepts --
type UserID int64prevents accidentally mixing user IDs with order IDs. - Use
_to skipiotavalue 0 when the zero value would be ambiguous.
Common Pitfalls¶
Silent integer overflow
Go does not panic on integer overflow. Values wrap around silently.
Short declaration shadowing
:= inside an if or for block creates a new variable that shadows the outer one.
Untyped constant overflow
Untyped constants have arbitrary precision, but overflow is checked at assignment.
nil map write panic
A zero-value map is nil. Reading returns the zero value, but writing panics.
Performance Considerations¶
intvs sized integers:intmatches the CPU word size, which is fastest for arithmetic. Useint32/int64only when interacting with external formats or saving memory in large slices.float64vsfloat32: Preferfloat64-- it's the default for numeric literals and mostmathfunctions.float32only saves memory in large datasets.- String concatenation: Strings are immutable. Repeated
+=creates O(n²) allocations. Usestrings.Builderfor building strings in loops. - Small structs vs pointers: For structs ≤ 3-4 fields of basic types, pass by value to avoid heap allocation. The copy is cheaper than the GC overhead.
Key Takeaways¶
- Go has two declaration styles:
var(anywhere) and:=(functions only). - Every type has a usable zero value -- no uninitialized variable bugs.
- There are no implicit type conversions -- all conversions are explicit.
iotagenerates sequential constants; skip 0 when the zero value is ambiguous.- Custom types (
type X int) create distinct types with method capability; aliases (type X = int) are interchangeable with the original. - Integer overflow is silent -- no runtime panic or error.