Types & Variables
📋 Jump to Takeaways🎁 Go has only 25 keywords — fewer than almost any modern language — yet its type system catches entire categories of bugs at compile time. How?
Go's type system is one of the reasons it catches bugs at compile time instead of 3 AM in production. There are no implicit conversions, no type coercion surprises, and every variable has a zero value, so nothing is ever "undefined."
var Declaration
The var keyword declares a variable with an explicit type. You'll use var when:
- You need a variable outside a function (package level)
- You want the zero value without assigning anything
- You want to specify the type explicitly
var name string = "Go"
var age int = 10
var active bool = trueYou can omit the value and get the zero value, or omit the type and let Go infer it.
var count int // 0 — useful when you just need the zero value
var label = "hi" // type inferred as stringShort Declaration
Inside functions, := is the preferred way. It's shorter and cleaner. But it only works inside functions.
func main() {
name := "Go" // string
age := 10 // int
ratio := 3.14 // float64
active := true // bool
}Use := by default. Reach for var when you have a reason to.
Basic Types
var s string = "hello"
var i int = 42
var i64 int64 = 9223372036854775807
var f float64 = 3.14
var b bool = true
var by byte = 'A' // alias for uint8
var r rune = '⚡' // alias for int32, represents a Unicode code pointZero Values
Uninitialized variables get a zero value. Go never has undefined or null surprises.
var s string // ""
var i int // 0
var f float64 // 0.0
var b bool // false
var p *int // nil
var sl []int // nil
var m map[string]int // nilConstants
Constants are declared with const. They cannot be changed after declaration.
const Pi = 3.14159
const AppName = "ByteLearn"Use iota for sequential constants (enums):
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
)Type Conversion
Go has no implicit type conversion. You must convert explicitly.
i := 42
f := float64(i) // int → float64
back := int(f) // float64 → int (truncates)Converting an int directly to a string interprets it as a Unicode code point, not a number. go vet (Go 1.15+) flags this as a likely mistake.
// ❌ produces "A" (code point 65), and go vet flags this as a likely mistake
ch := string(65)
// ✅ use strconv or fmt.Sprintf for number-to-string
import "strconv"
s := strconv.Itoa(65) // "65"
s2 := fmt.Sprintf("%d", 65) // "65"
// ✅ use rune conversion when you actually want the character
ch := string(rune(65)) // "A" — explicit intent, no warningFormat Verbs
fmt.Printf uses verbs to format values. Each verb matches a type.
name := "Go"
version := 2
pi := 3.14159
debug := true
fmt.Printf("Language: %s\n", name) // Language: Go
fmt.Printf("Version: %d\n", version) // Version: 2
fmt.Printf("Pi: %f\n", pi) // Pi: 3.141590
fmt.Printf("Pi: %.2f\n", pi) // Pi: 3.14
fmt.Printf("Debug: %t\n", debug) // Debug: true%v is the catch-all — it prints any value in its default format.
fmt.Printf("%v %v %v\n", name, version, pi) // Go 2 3.14159Use %T to print a value's type. Useful for debugging.
fmt.Printf("%T\n", name) // string
fmt.Printf("%T\n", version) // int
fmt.Printf("%T\n", pi) // float64%q adds quotes around strings and escapes special characters.
path := "C:\\Users\\file.txt"
fmt.Printf("%s\n", path) // C:\Users\file.txt
fmt.Printf("%q\n", path) // "C:\\Users\\file.txt"For padding and alignment, use width specifiers.
fmt.Printf("|%10s|\n", "right") // | right|
fmt.Printf("|%-10s|\n", "left") // |left |
fmt.Printf("|%05d|\n", 42) // |00042|Sprintf returns the formatted string instead of printing it.
msg := fmt.Sprintf("Hello, %s! You are %d.", "Alice", 30)
fmt.Println(msg) // Hello, Alice! You are 30.Quick reference:
| Verb | Purpose | Example |
|---|---|---|
%s |
string | "hello" |
%d |
integer | 42 |
%f |
float (default precision) | 3.141590 |
%.2f |
float (2 decimal places) | 3.14 |
%t |
boolean | true |
%v |
any value (default format) | works on everything |
%T |
type of a value | int, string |
%q |
quoted string | "hello" |
%p |
pointer address | 0xc0000b6010 |
%w |
wrap error (Errorf only) | preserves error chain |
Defined Types vs Type Aliases
Go has two ways to give a type a new name, and they behave very differently.
Defined types (type Celsius float64) create a new distinct type. Celsius and float64 are not interchangeable — the compiler rejects mixing them without an explicit conversion. You can add methods to a defined type.
Type aliases (type Celsius = float64) just give an existing type another name. They are the exact same type — fully interchangeable, no conversion needed, no new methods.
| Defined type | Type alias | |
|---|---|---|
| Syntax | type T U |
type T = U |
| Same as base type? | ❌ distinct type | ✅ same type |
| Explicit conversion needed? | ✅ required | ❌ not needed |
| Can add methods? | ✅ | ❌ |
| Use for | Domain types, units, IDs | Refactoring, API shims |
In practice you'll almost always use defined types. Aliases are mainly for large-scale renaming where two packages need to refer to the same type during a transition.
// Defined types — distinct, compile-time safe
type Celsius float64
type Fahrenheit float64
var c Celsius = 100.0
var f Fahrenheit = 212.0
// c + f // ❌ compile error: mismatched types
c + Celsius(f) // ✅ explicit conversion required
// Defined types can have methods
func (c Celsius) String() string {
return fmt.Sprintf("%.1f°C", float64(c))
}
// Type aliases — same type, interchangeable
type MyFloat = float64
var x MyFloat = 3.14
var y float64 = x // ✅ no conversion needed — they are the same typeKey Takeaways
varfor explicit declarations;:=for short declarations inside functions- Basic types:
string,int,float64,bool,byte,rune - Zero values:
0,"",false,nil— no uninitialized surprises constwithiotacreates clean enumerations- Go requires explicit type conversion — no implicit casting
- Format verbs:
%sstring,%dint,%ffloat,%tbool,%vany value,%Ttype Sprintfreturns a formatted string;Printfprints it- Defined types (
type T U) create a new distinct type — no implicit conversion, can have methods; use for domain types like units or IDs - Type aliases (
type T = U) are just another name for the same type — fully interchangeable, mainly used for refactoring
🎁 Next up: Go functions can return multiple values — and that single feature eliminated the need for exceptions entirely.