Part II · Types & Data
Types & Variables
Zero values instead of null, explicit integer sizes, no implicit conversions, and enums built from named types and iota.
Section 03 of 31 7 min read
Go is statically typed like Java, but stricter in some places and simpler in others. Every variable has a usable zero value, integer sizes are explicit, and there are no implicit numeric conversions at all.
Declaring variables
int x = 10;
int y; // field: 0; local: must assign first
String s; // field: null (!)
var z = 10; // Java 10+: local type inference
final int MAX = 3;var x int = 10 // explicit type and value
var y int // zero value: 0
var s string // zero value: "" (never nil)
z := 10 // short form: inferred, functions only
const Max = 3 // compile-time constant
var ( // grouped declarations
host = "localhost"
port = 8080
)Java: a field before assignment
Go: the zero value of each type
null. Every Go type has a ready-to-use zero value, and only pointers, slices, maps, channels, functions and interfaces can be nil.Integer types: size is explicit
// Java: byte(8) short(16) int(32) long(64), all signed, plus char(16, unsigned)
// Go: signed and unsigned, named by bit width
var a int // 64-bit on 64-bit platforms. The default choice.
var b int32 // ±2.1 billion. Alias: rune (a Unicode code point)
var c int64 // ±9.2 quintillion. IDs, Unix timestamps
var d uint8 // 0–255. Alias: byte (raw binary data)
var e uint64 // bit masks, hashes, IDs from external systems
// Quick guide:
// int counters, indexes, lengths (len() returns int)
// int64 database keys, time.Duration, anything on the wire
// byte binary data: []byte is the standard buffer type
// rune one Unicode code point (see the Strings section)
// uint* bit manipulation and wire formats, not "can't be negative"No implicit conversions
int count = 3;
long total = count; // implicit widening
double avg = total / 2.0; // implicit promotion
Integer boxed = count; // autoboxing
String s = "n=" + count; // implicit toStringcount := 3
var total int64 = int64(count) // must convert
avg := float64(total) / 2.0 // must convert
// There is no boxing: int is always a value.
s := "n=" + strconv.Itoa(count) // no implicit toString
s2 := fmt.Sprintf("n=%d", count)
// var t int64 = count // compile error: int ≠ int64Named types and enums with iota
public enum Status {
IDLE, ACTIVE, DRAINING, STOPPED;
}
Status s = Status.ACTIVE;
switch (s) {
case ACTIVE -> start();
default -> {}
}// A distinct type, not an alias for int
type Status int
const (
StatusIdle Status = iota // 0
StatusActive // 1
StatusDraining // 2
StatusStopped // 3
)
s := StatusActive
switch s {
case StatusActive:
start()
}func SetStatus(s Status) {}
SetStatus(StatusActive) // ok
SetStatus(2) // ok: untyped constant converts implicitly
n := 2
SetStatus(n) // compile error: int is not Status
SetStatus(Status(n)) // ok: explicit conversion, even if n is 42// Java enums have name() for free. In Go, implement fmt.Stringer:
func (s Status) String() string {
switch s {
case StatusIdle:
return "idle"
case StatusActive:
return "active"
case StatusDraining:
return "draining"
case StatusStopped:
return "stopped"
}
return fmt.Sprintf("Status(%d)", int(s))
}
fmt.Println(StatusDraining) // "draining": fmt calls String()
// For long enums, generate it instead:
//go:generate go tool stringer -type=StatusDefined types vs type aliases
type UserID int64 // defined type: new, distinct type
type Celsius float64 // can have its own methods
type Bytes = []byte // alias: just another name, same type
// cannot add methods to it
var id UserID = 42
var n int64 = id // compile error: UserID is not int64
var m int64 = int64(id) // ok
var b Bytes = []byte("hi") // ok: identical types