Go Programming from Scratch with 25 Examples
Go (Golang) is a language developed at Google. It is simple, compiles quickly and supports concurrency built in, which makes it popular for servers, tools and cloud systems. This article has 25 code examples, each run with Go 1.27, with the real output of each one. The examples move from the basics to concurrency, web handlers and generics.
What you need
Install Go from go.dev/dl and check with `go version`. Create a `.go` file and run it with `go run filename.go`.
Illustrations
Example 1: Hello, world
The simplest Go program. Every program starts with the main package and a main function.
package main
import "fmt"
func main() {
fmt.Println("สวัสดี Go")
}
**Real output**
สวัสดี Go
Example 2: Variables and constants
`:=` declares and assigns in one step, `var` declares explicitly, and `const` is for values that never change.
package main
import "fmt"
const schoolName = "โรงเรียนตัวอย่าง"
func main() {
name := "สมชาย"
var age int = 20
fmt.Println(schoolName, name, age)
}
**Real output**
โรงเรียนตัวอย่าง สมชาย 20
Example 3: Zero values
A variable declared without a value gets the zero value of its type, such as 0, an empty string or false.
package main
import "fmt"
func main() {
var n int
var s string
var b bool
var f float64
fmt.Printf("int=%d string=%q bool=%t float=%.1f\n", n, s, b, f)
}
**Real output**
int=0 string="" bool=false float=0.0
Example 4: if and else
Decisions with if and else if. Go does not need parentheses around the condition.
package main
import "fmt"
func main() {
score := 72
if score >= 80 {
fmt.Println("A")
} else if score >= 70 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
**Real output**
B
Example 5: for loops
Go has only one loop keyword, for, which can count or act like a while loop.
package main
import "fmt"
func main() {
for i := 1; i <= 3; i++ {
fmt.Println("นับ", i)
}
n := 0
for n < 2 {
n++
}
fmt.Println("n =", n)
}
**Real output**
นับ 1
นับ 2
นับ 3
n = 2
Example 6: switch
switch compares several cases and does not fall through to the next case unless you ask it to.
package main
import "fmt"
func main() {
day := "เสาร์"
switch day {
case "เสาร์", "อาทิตย์":
fmt.Println("วันหยุด")
default:
fmt.Println("วันทำงาน")
}
}
**Real output**
วันหยุด
Example 7: Arrays and slices
Arrays have a fixed size. Slices grow with append and can be cut with s[a:b].
package main
import "fmt"
func main() {
arr := [3]int{1, 2, 3}
s := []int{10, 20}
s = append(s, 30, 40)
fmt.Println("array:", arr, "len:", len(arr))
fmt.Println("slice:", s, "len:", len(s), "cap:", cap(s) >= 4)
fmt.Println("ตัดช่วง:", s[1:3])
}
**Real output**
array: [1 2 3] len: 3
slice: [10 20 30 40] len: 4 cap: true
ตัดช่วง: [20 30]
Example 8: Maps
Maps store key-value pairs. The form `v, ok :=` checks whether a key exists.
package main
import "fmt"
func main() {
scores := map[string]int{"สมชาย": 85, "สมหญิง": 72}
scores["สมศักดิ์"] = 45
if v, ok := scores["สมชาย"]; ok {
fmt.Println("คะแนน:", v)
}
delete(scores, "สมหญิง")
fmt.Println("จำนวน:", len(scores))
}
**Real output**
คะแนน: 85
จำนวน: 2
Example 9: Functions with several return values
A function can return several values. An error is usually returned as the last value.
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("หารด้วยศูนย์ไม่ได้")
}
return a / b, nil
}
func main() {
q, err := divide(10, 2)
fmt.Println("ผล:", q, "error:", err)
_, err = divide(1, 0)
fmt.Println("error:", err)
}
**Real output**
ผล: 5 error: <nil>
error: หารด้วยศูนย์ไม่ได้
Example 10: Closures
A function that remembers variables from its surrounding scope. Useful for counters and private state.
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
next := counter()
fmt.Println(next(), next(), next())
}
**Real output**
1 2 3
Example 11: Structs and methods
A struct groups several pieces of data, and methods attach behaviour to that type.
package main
import "fmt"
type Student struct {
Name string
Score int
}
func (s Student) Grade() string {
if s.Score >= 80 {
return "A"
}
return "B"
}
func main() {
s := Student{Name: "สมชาย", Score: 85}
fmt.Println(s.Name, s.Grade())
}
**Real output**
สมชาย A
Example 12: Interfaces
An interface lists the methods a type must have. Any type with those methods satisfies the interface automatically.
package main
import "fmt"
type Shape interface {
Area() float64
}
type Rect struct{ W, H float64 }
func (r Rect) Area() float64 { return r.W * r.H }
func main() {
var s Shape = Rect{W: 3, H: 4}
fmt.Printf("พื้นที่ = %.1f\n", s.Area())
}
**Real output**
พื้นที่ = 12.0
Example 13: Pointers
&x gives the address of a variable and *p reads or changes the value at that address, so a function can change caller data.
package main
import "fmt"
func increment(n *int) {
*n++
}
func main() {
x := 5
increment(&x)
fmt.Println("x =", x)
}
**Real output**
x = 6
Example 14: Handling errors
Create errors with fmt.Errorf and %w, then check with errors.Is to see whether an error matches a known one.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("ไม่พบข้อมูล")
func find(id int) error {
if id != 1 {
return fmt.Errorf("รหัส %d: %w", id, ErrNotFound)
}
return nil
}
func main() {
err := find(2)
fmt.Println(err)
fmt.Println("ตรงกับ ErrNotFound:", errors.Is(err, ErrNotFound))
}
**Real output**
รหัส 2: ไม่พบข้อมูล
ตรงกับ ErrNotFound: true
Example 15: defer
defer postpones a call until the surrounding function returns. It suits closing files and releasing resources.
package main
import "fmt"
func main() {
defer fmt.Println("ทำงานสุดท้าย")
fmt.Println("เริ่มต้น")
fmt.Println("ประมวลผล")
}
**Real output**
เริ่มต้น
ประมวลผล
ทำงานสุดท้าย
Example 16: panic and recover
panic stops normal execution, and recover can catch it only inside a deferred function, so the program does not crash.
package main
import "fmt"
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("กู้คืนจาก panic: %v", r)
}
}()
return a / b, nil
}
func main() {
fmt.Println(safeDivide(10, 2))
fmt.Println(safeDivide(1, 0))
}
**Real output**
5 <nil>
0 กู้คืนจาก panic: runtime error: integer divide by zero
Example 17: Goroutines
The go keyword starts work that runs concurrently. sync.WaitGroup waits until all the work is done.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
_ = n * n
}(i)
}
wg.Wait()
fmt.Println("ทุกงานเสร็จแล้ว")
}
**Real output**
ทุกงานเสร็จแล้ว
Example 18: Channels
Channels pass values between goroutines. This example sends values from a goroutine to main, then closes the channel.
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 1; i <= 3; i++ {
ch <- i * 10
}
close(ch)
}()
for v := range ch {
fmt.Println("ได้รับ", v)
}
}
**Real output**
ได้รับ 10
ได้รับ 20
ได้รับ 30
Example 19: select and timeouts
select waits on several channels at once. time.After sets a maximum waiting time.
package main
import (
"fmt"
"time"
)
func main() {
result := make(chan string)
go func() {
time.Sleep(200 * time.Millisecond)
result <- "เสร็จ"
}()
select {
case msg := <-result:
fmt.Println(msg)
case <-time.After(50 * time.Millisecond):
fmt.Println("หมดเวลา")
}
}
**Real output**
หมดเวลา
Example 20: Mutex for shared data
sync.Mutex lets many goroutines change one variable safely. The total is always correct.
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
var wg sync.WaitGroup
total := 0
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
total++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("รวม =", total)
}
**Real output**
รวม = 100
Example 21: strings package and runes
strings provides text functions. len counts bytes, so count Thai characters by converting to []rune.
package main
import (
"fmt"
"strings"
)
func main() {
s := "Go เขียนง่าย"
fmt.Println(strings.ToUpper("go"), strings.Contains(s, "เขียน"))
fmt.Println(strings.Split("a,b,c", ","), len("สวัสดี"))
fmt.Println([]rune("สวัสดี")[0] == 'ส')
}
**Real output**
GO true
[a b c] 18
true
Example 22: Reading and writing files
os.WriteFile and os.ReadFile write and read a whole file in one call.
package main
import (
"fmt"
"os"
)
func main() {
if err := os.WriteFile("note.txt", []byte("บันทึกจาก Go\n"), 0644); err != nil {
fmt.Println("เขียนไม่ได้:", err)
return
}
data, err := os.ReadFile("note.txt")
if err != nil {
fmt.Println("อ่านไม่ได้:", err)
return
}
fmt.Print("อ่านได้: ", string(data))
}
**Real output**
อ่านได้: บันทึกจาก Go
Example 23: JSON
encoding/json converts a struct to JSON and back. Tags such as `json:"..."` set the field names.
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"name"`
Score int `json:"score"`
}
func main() {
b, _ := json.Marshal(Student{Name: "สมชาย", Score: 85})
fmt.Println(string(b))
var s Student
_ = json.Unmarshal([]byte(`{"name":"สมหญิง","score":72}`), &s)
fmt.Println(s.Name, s.Score)
}
**Real output**
{"name":"สมชาย","score":85}
สมหญิง 72
Example 24: Testing an HTTP handler
httptest tests a handler function without starting a real server.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "สวัสดีจาก Go HTTP")
}
func main() {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
hello(rec, req)
body, _ := io.ReadAll(rec.Result().Body)
fmt.Println(rec.Code, string(body))
}
**Real output**
200 สวัสดีจาก Go HTTP
Example 25: Generics
Functions that work on several types using a type parameter T, constrained by an interface.
package main
import "fmt"
type Number interface {
~int | ~float64
}
func Sum[T Number](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
func main() {
fmt.Println(Sum([]int{1, 2, 3}))
fmt.Println(Sum([]float64{1.5, 2.5}))
}
**Real output**
6
4
Next steps
Try writing a web server that receives JSON and saves it to a file. Then study the net/http and context packages for timeouts and cancellation.
Summary
Go has a simple syntax, checks types at compile time, handles errors as values, and supports concurrency with goroutines and channels, which makes it a good fit for servers and workloads with many tasks.