Updated Aug 7, 2026

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 = true

You 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 string

Short 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 point

Zero 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 // nil

Constants

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 warning

Format 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.14159

Use %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 type

Key Takeaways

  • var for explicit declarations; := for short declarations inside functions
  • Basic types: string, int, float64, bool, byte, rune
  • Zero values: 0, "", false, nil — no uninitialized surprises
  • const with iota creates clean enumerations
  • Go requires explicit type conversion — no implicit casting
  • Format verbs: %s string, %d int, %f float, %t bool, %v any value, %T type
  • Sprintf returns a formatted string; Printf prints 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.

🚀 Ready to run?

Complete runnable examples for this lesson.

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

Spot something off? Report an issue
© 2026 ByteLearn.dev. Free courses for developers. · Privacy