Go 语言从零开始:25 个示例

Go 语言从零开始:25 个示例

Go(Golang)是 Google 开发的语言。它语法简洁、编译快,并内置并发(concurrency)支持,因此广泛用于服务器、工具和云系统。本文有 25 个代码示例,全部使用 Go 1.27 运行,并展示每个示例的真实输出。内容从基础讲到并发、Web 处理函数和泛型。

需要准备的东西

从 go.dev/dl 安装 Go,并用 `go version` 检查。创建 `.go` 文件,用 `go run 文件名.go` 运行。

示意图

下面的示意图展示了工作流程,以及协程与通道的概念(为示意图,不是实际截图)。
下面的示意图展示了工作流程,以及协程与通道的概念(为示意图,不是实际截图)。

示例 1: 你好,世界

最简单的 Go 程序。每个程序都从 main 包和 main 函数开始。

package main

import "fmt"

func main() {
	fmt.Println("สวัสดี Go")
}

**实际输出**

สวัสดี Go

示例 2: 变量与常量

`:=` 同时声明并赋值,`var` 明确声明,`const` 用于永不改变的值。

package main

import "fmt"

const schoolName = "โรงเรียนตัวอย่าง"

func main() {
	name := "สมชาย"
	var age int = 20
	fmt.Println(schoolName, name, age)
}

**实际输出**

โรงเรียนตัวอย่าง สมชาย 20

示例 3: 零值

声明但未赋值的变量会得到其类型的零值,例如 0、空字符串或 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)
}

**实际输出**

int=0 string="" bool=false float=0.0

示例 4: if 与 else

使用 if 和 else if 做判断。条件外不需要括号。

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")
	}
}

**实际输出**

B

示例 5: for 循环

Go 只有一种循环关键字 for,既可以计数,也可以像 while 一样使用。

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)
}

**实际输出**

นับ 1
นับ 2
นับ 3
n = 2

示例 6: switch

switch 比较多个分支,除非明确要求,否则不会落入下一个 case,因此不需要 break。

package main

import "fmt"

func main() {
	day := "เสาร์"
	switch day {
	case "เสาร์", "อาทิตย์":
		fmt.Println("วันหยุด")
	default:
		fmt.Println("วันทำงาน")
	}
}

**实际输出**

วันหยุด

示例 7: 数组与切片

数组大小固定。切片可以用 append 增长,并可用 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])
}

**实际输出**

array: [1 2 3] len: 3
slice: [10 20 30 40] len: 4 cap: true
ตัดช่วง: [20 30]

示例 8: 映射(map)

map 保存键值对。使用 `v, ok :=` 形式可以检查键是否存在。

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))
}

**实际输出**

คะแนน: 85
จำนวน: 2

示例 9: 多返回值函数

函数可以返回多个值,通常把 error 作为最后一个返回值。

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)
}

**实际输出**

ผล: 5 error: <nil>
error: หารด้วยศูนย์ไม่ได้

示例 10: 闭包

能记住外层作用域变量的函数,适合做计数器或私有状态。

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())
}

**实际输出**

1 2 3

示例 11: 结构体与方法

struct 把多个数据组合在一起,方法则为该类型添加行为。

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())
}

**实际输出**

สมชาย A

示例 12: 接口

接口定义类型必须具备的方法集合。拥有这些方法的类型会自动满足该接口。

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())
}

**实际输出**

พื้นที่ = 12.0

示例 13: 指针

&x 得到变量的地址,*p 读写该地址上的值,使函数能修改调用者的数据。

package main

import "fmt"

func increment(n *int) {
	*n++
}

func main() {
	x := 5
	increment(&x)
	fmt.Println("x =", x)
}

**实际输出**

x = 6

示例 14: 错误处理

用 fmt.Errorf 和 %w 创建错误,再用 errors.Is 判断它是否属于某个已知错误。

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))
}

**实际输出**

รหัส 2: ไม่พบข้อมูล
ตรงกับ ErrNotFound: true

示例 15: defer

defer 会把调用推迟到函数返回时执行,适合关闭文件和释放资源。

package main

import "fmt"

func main() {
	defer fmt.Println("ทำงานสุดท้าย")
	fmt.Println("เริ่มต้น")
	fmt.Println("ประมวลผล")
}

**实际输出**

เริ่มต้น
ประมวลผล
ทำงานสุดท้าย

示例 16: panic 与 recover

panic 会中断正常执行,recover 只能在 defer 的函数中捕获它,从而避免整个程序崩溃。

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))
}

**实际输出**

5 <nil>
0 กู้คืนจาก panic: runtime error: integer divide by zero

示例 17: 协程(goroutine)

go 关键字启动并发执行的任务,sync.WaitGroup 等待所有任务完成。

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("ทุกงานเสร็จแล้ว")
}

**实际输出**

ทุกงานเสร็จแล้ว

示例 18: 通道(channel)

通道在协程之间传递值。本例把值从协程发送到 main,然后关闭通道。

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)
	}
}

**实际输出**

ได้รับ 10
ได้รับ 20
ได้รับ 30

示例 19: select 与超时

select 可以同时等待多个通道。time.After 用来设置最长等待时间。

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("หมดเวลา")
	}
}

**实际输出**

หมดเวลา

示例 20: 用 Mutex 保护共享数据

sync.Mutex 让多个协程安全地修改同一个变量,最终的总和始终正确。

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)
}

**实际输出**

รวม = 100
下面的示意图展示了工作流程,以及协程与通道的概念(为示意图,不是实际截图)。
下面的示意图展示了工作流程,以及协程与通道的概念(为示意图,不是实际截图)。

示例 21: strings 包与 rune

strings 提供字符串处理函数。len 统计的是字节数,统计泰语字符需要先转换为 []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] == 'ส')
}

**实际输出**

GO true
[a b c] 18
true

示例 22: 读写文件

os.WriteFile 与 os.ReadFile 可以用一次调用读写整个文件。

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))
}

**实际输出**

อ่านได้: บันทึกจาก Go

示例 23: JSON

encoding/json 可以在 struct 与 JSON 之间互相转换,`json:"..."` 标签用于指定字段名。

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)
}

**实际输出**

{"name":"สมชาย","score":85}
สมหญิง 72

示例 24: 测试 HTTP 处理函数

httptest 可以在不启动真实服务器的情况下测试处理函数。

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))
}

**实际输出**

200 สวัสดีจาก Go HTTP

示例 25: 泛型

使用类型参数 T 编写可用于多种类型的函数,并用接口约束类型。

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}))
}

**实际输出**

6
4

下一步

尝试编写一个接收 JSON 并保存到文件的 Web 服务器。然后学习 net/http 与 context 包,用于超时和取消操作。

总结

Go 语法简洁,类型在编译时检查,错误作为值处理,并通过协程和通道支持并发,因此很适合服务器和需要处理大量任务的场景。