Go Fundamentals Basics
10 examples to get you started with Go Fundamentals - 7 basic and 3 intermediate.
Prerequisites
- Install Go 1.26.x or later from go.dev/dl.
- Create a module:
mkdir hello && cd hello && go mod init example.com/hello. - Run any snippet with
go run .after saving it asmain.goin that directory.
Basic Examples
1. Hello, main
The smallest executable: a main package with func main().
package main
import "fmt"
func main() {
fmt.Println("hello, Go")
}package mainmarks an executable, not a library.import "fmt"brings in the formatted I/O package from the standard library.go run .compiles and runs without leaving a binary in the directory.
Related: How Go Programs Are Structured - packages, linking, and the toolchain
2. Variables and short declaration
Declare with var or use := inside functions.
package main
import "fmt"
func main() {
var name string = "Go"
year := 2009
fmt.Println(name, year)
}:=infers type and can only appear inside functions.varworks at package level;:=does not.- Unused variables are compile errors - Go enforces clean locals.
Related: Variables, Constants & Scope - declaration styles and scope rules
3. Zero values
Variables get a default without explicit initialization.
package main
import "fmt"
func main() {
var count int
var ready bool
var label string
fmt.Printf("int=%d bool=%t string=%q\n", count, ready, label)
}- Numeric types default to
0,booltofalse, strings to"". - Pointers, slices, maps, channels, and interfaces default to
nil. - Rely on zero values for "empty" sentinel states when that is idiomatic.
Related: Zero Values and Initialization - per-type defaults and init patterns
4. Constants and iota
const names compile-time values; iota builds enumerated constants.
package main
import "fmt"
const (
Sunday = iota
Monday
Tuesday
)
func main() {
fmt.Println(Sunday, Monday, Tuesday) // 0 1 2
}iotaresets to 0 in eachconstblock and increments per line.- Constants must be expressible at compile time (no runtime calls).
- Use typed constants when you need a distinct type, not just an untyped number.
Related: Variables, Constants & Scope -
iotapatterns and scope
5. Basic types and string iteration
Strings are UTF-8 byte sequences; iterate by rune with range.
package main
import "fmt"
func main() {
s := "café"
for i, r := range s {
fmt.Printf("%d: %U\n", i, r)
}
}rangeover a string yields byte index and rune (int32), not byte offset for multi-byte chars.len(s)counts bytes, not runes.- Use
[]rune(s)when you need random access by character index.
Related: Numeric Types, Strings, Runes & Byte Slices - UTF-8, runes, and conversions
6. Slices
Slices are dynamic views over an underlying array.
package main
import "fmt"
func main() {
nums := []int{1, 2, 3}
nums = append(nums, 4)
fmt.Println(nums, len(nums), cap(nums))
}appendmay allocate a new backing array when capacity is exceeded.lenis element count;capis backing array size from the slice header.- A
nilslice is valid and works withappendandrange.
Related: Arrays, Slices & Maps at a Glance - len/cap and sharing
7. Maps
Maps are hash tables keyed by comparable types.
package main
import "fmt"
func main() {
ages := map[string]int{"ada": 36}
ages["linus"] = 55
if v, ok := ages["grace"]; ok {
fmt.Println(v)
} else {
fmt.Println("grace: unknown")
}
}- The two-value form
v, ok := m[key]distinguishes missing keys from zero values. - Maps must be initialized with
makeor a literal before assignment. - Iteration order is randomized - do not depend on key order.
Related: Arrays, Slices & Maps at a Glance - map iteration and gotchas
Intermediate Examples
8. Structs and embedding
Group fields in a struct; embed to promote methods and fields.
package main
import "fmt"
type Person struct{ Name string }
type Employee struct {
Person
ID int
}
func main() {
e := Employee{Person: Person{Name: "Grace"}, ID: 1}
fmt.Println(e.Name, e.ID) // promoted field
}- Embedded fields promote to the outer type's namespace.
- Embedding is composition, not classical inheritance.
- Use explicit field names when promotion would be ambiguous.
Related: Structs, Embedding & Field Promotion - modeling and promotion rules
9. Pointers
& takes an address; * dereferences. Methods can use pointer receivers to mutate.
package main
import "fmt"
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ }
func main() {
c := &Counter{}
c.Inc()
fmt.Println(c.n)
}- Go passes structs by value unless you pass a pointer.
- Pointer receivers avoid copying large structs and allow mutation.
nilpointers panic on dereference - check before use.
Related: Pointers & the Address-of Operator - when pointers help vs hurt
10. Package layout sketch
Split main from reusable code across packages in one module.
// main.go
package main
import "example.com/hello/greet"
func main() {
greet.Hello("world")
}// greet/greet.go
package greet
import "fmt"
func Hello(name string) {
fmt.Printf("hello, %s\n", name)
}- Only
mainpackages build executables;greetis a library package. - Exported names start with uppercase (
Hello); lowercase stays private. - Run from module root:
go run .with both files in place.
Related: How Go Programs Are Structured - modules, imports, and
internal/
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).