前言

Go(又称 Golang)是由 Google 开发的静态强类型、编译型语言。它语法简洁、并发能力强、编译速度快,广泛应用于后端服务、云原生、微服务、CLI 工具等领域。知名的 Docker、Kubernetes、Prometheus 都是用 Go 编写的。

本文将从零开始,带你掌握 Go 语言的核心基础。

为什么选择 Go?

优势 说明
🚀 编译快 大型项目几秒内编译完成
🔒 类型安全 静态类型,编译期发现错误
🧵 原生并发 goroutine + channel,简洁高效的并发模型
📦 依赖简单 代码格式化、测试、依赖管理都内置于工具链
🪶 部署简单 编译成单个二进制文件,无需运行时环境

一、环境搭建

1.1 安装 Go

go.dev/dl 下载对应系统的安装包,或使用包管理器安装。

Linux(apt):

1
sudo apt install golang-go

macOS(Homebrew):

1
brew install go

1.2 验证安装

1
2
go version
# 输出示例:go version go1.22.0 linux/amd64

1.3 配置环境(可选)

Go 使用 GOPATHGOMODULE 管理代码。现代 Go 通常使用模块模式:

1
2
go env GOPATH   # 查看 GOPATH
go env GOROOT # 查看 Go 安装路径

二、第一个 Go 程序

1
2
3
4
5
6
7
8
// main.go
package main

import "fmt"

func main() {
fmt.Println("Hello, 七月小站!")
}

运行

1
2
3
4
5
6
7
# 直接运行
go run main.go
# 输出:Hello, 七月小站!

# 编译为二进制
go build main.go
./main

程序结构解读:

代码 含义
package main 声明包名,可执行程序必须是 main
import "fmt" 导入格式化输出包
func main() 程序入口函数

三、变量与常量

3.1 变量声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import "fmt"

func main() {
// 方式一:标准声明
var name string = "July"
var age int = 25

// 方式二:类型推断
var city = "深圳"
var score = 99.5

// 方式三:短变量声明(最常用,仅函数内可用)
language := "Go"
count := 10

// 方式四:批量声明
var (
host string = "localhost"
port int = 8080
debug bool = false
)

// 多变量赋值
a, b, c := 1, 2, 3

fmt.Println(name, age, city, score, language, count)
fmt.Println(host, port, debug, a, b, c)
}

3.2 常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const PI = 3.14159
const (
StatusOK = 200
StatusError = 500
)

// iota 枚举器
const (
Monday = iota // 0
Tuesday // 1
Wednesday // 2
Thursday // 3
Friday // 4
Saturday // 5
Sunday // 6
)

四、基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 布尔型
var isActive bool = true

// 整型
var i8 int8 = 127 // -128 ~ 127
var u8 uint8 = 255 // 0 ~ 255
var i16 int16 = 32767
var i32 int32 = 2147483647
var i64 int64 = 9223372036854775807
var i int = 100 // 根据平台决定 32 或 64 位
var u uint = 200 // 无符号整数

// 浮点型
var f32 float32 = 3.14
var f64 float64 = 3.141592653589793

// 字符串
var s1 string = "Hello"
s2 := "七月小站"

// 字节与字符
var b byte = 'A' // byte 是 uint8 的别名
var r rune = '中' // rune 是 int32 的别名,代表 Unicode 码点

字符串常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import (
"fmt"
"strings"
)

func main() {
s := "Hello, Go!"

fmt.Println(len(s)) // 长度: 10
fmt.Println(strings.Contains(s, "Go")) // 包含: true
fmt.Println(strings.HasPrefix(s, "He")) // 前缀: true
fmt.Println(strings.ToUpper(s)) // 大写
fmt.Println(strings.Split(s, ",")) // 分割: [Hello Go!]
fmt.Println(strings.ReplaceAll(s, "Go", "世界")) // 替换
fmt.Println(strings.TrimSpace(" hi ")) // 去空格
}

类型转换

Go 不支持隐式类型转换,必须显式转换:

1
2
3
4
5
6
7
8
9
10
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint

// 字符串和数字互转
import "strconv"

s := strconv.Itoa(42) // int → string: "42"
n, _ := strconv.Atoi("42") // string → int: 42
f := strconv.FormatFloat(3.14, 'f', 2, 64) // float64 → string: "3.14"

五、复合数据类型

5.1 数组(固定长度)

1
2
3
4
5
6
7
8
9
10
11
var arr1 [5]int                        // [0 0 0 0 0]
arr2 := [3]string{"Go", "Python", "Java"}
arr3 := [...]int{1, 2, 3, 4, 5} // 编译器自动推断长度

fmt.Println(arr2[0]) // 访问元素
fmt.Println(len(arr3)) // 长度

// 遍历
for i, v := range arr2 {
fmt.Printf("索引: %d, 值: %s\n", i, v)
}

5.2 切片(动态数组,最常用)⭐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建切片
s1 := []int{1, 2, 3, 4, 5} // 字面量
s2 := make([]int, 3, 5) // make(类型, 长度, 容量)
var s3 []int // nil 切片

// 从数组/切片截取
arr := [5]int{1, 2, 3, 4, 5}
s4 := arr[1:4] // [2, 3, 4]
s5 := arr[:3] // [1, 2, 3]
s6 := arr[2:] // [3, 4, 5]

// 追加元素
s1 = append(s1, 6, 7, 8)
s1 = append(s1, s2...) // 追加另一个切片(需要...展开)

// 复制
dst := make([]int, len(s1))
copy(dst, s1)

// 删除元素(如删除索引 2)
s1 = append(s1[:2], s1[3:]...)

fmt.Println(len(s1), cap(s1)) // 长度 和 容量

💡 切片 vs 数组:数组长度固定,切片长度可变。Go 中绝大多数场景用切片。

5.3 Map(字典/映射)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建 map
m1 := map[string]int{
"Go": 100,
"Python": 200,
}
m2 := make(map[string]string)

// 增删改查
m2["name"] = "July" // 增/改
m2["blog"] = "blog.iot2045.cn"
delete(m1, "Python") // 删
value, ok := m2["name"] // 查(ok 判断 key 是否存在)
fmt.Println(value, ok) // July true

// 遍历
for k, v := range m2 {
fmt.Printf("key: %s, value: %s\n", k, v)
}

// 判断 key 是否存在
if _, ok := m2["email"]; !ok {
fmt.Println("email 不存在")
}

六、流程控制

6.1 if / else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
score := 85

if score >= 90 {
fmt.Println("优秀")
} else if score >= 80 {
fmt.Println("良好")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}

// 带初始化语句的 if
if err := doSomething(); err != nil {
fmt.Println("出错了:", err)
}

6.2 switch

Go 的 switch 不需要 break,默认只执行匹配的分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
day := 3

switch day {
case 1:
fmt.Println("星期一")
case 2:
fmt.Println("星期二")
case 3:
fmt.Println("星期三")
default:
fmt.Println("其他")
}

// 无表达式的 switch(替代 if-else 链)
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 60:
fmt.Println("C")
default:
fmt.Println("D")
}

// fallthrough:继续执行下一个 case
switch num := 1; num {
case 1:
fmt.Println("一")
fallthrough
case 2:
fmt.Println("二") // 也会执行
}

6.3 for 循环

Go 只有 for 一种循环,但可以模拟各种形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 标准 for
for i := 0; i < 5; i++ {
fmt.Println(i)
}

// 类似 while
count := 0
for count < 5 {
fmt.Println(count)
count++
}

// 无限循环
for {
// 需要 break 退出
break
}

// for range 遍历
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}

// 只要索引
for i := range nums {
fmt.Println(i)
}

// 只要值(用 _ 忽略索引)
for _, v := range nums {
fmt.Println(v)
}

6.4 break / continue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// break:跳出循环
for i := 0; i < 10; i++ {
if i == 5 {
break
}
fmt.Println(i) // 0 1 2 3 4
}

// continue:跳过当前迭代
for i := 0; i < 5; i++ {
if i == 2 {
continue
}
fmt.Println(i) // 0 1 3 4
}

// 标签跳转(跳出多层循环)
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer
}
fmt.Printf("(%d, %d) ", i, j)
}
}
// 输出: (0, 0) (0, 1) (0, 2) (1, 0)

七、函数

7.1 基本函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 无返回值
func sayHello(name string) {
fmt.Printf("Hello, %s!\n", name)
}

// 单个返回值
func add(a, b int) int {
return a + b
}

// 多个返回值(Go 的特色!)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}

// 命名返回值
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 裸返回(自动返回命名返回值)
}

// 可变参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}

func main() {
sayHello("July")
fmt.Println(add(3, 5))

result, err := divide(10, 3)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}

fmt.Println(sum(1, 2, 3, 4, 5)) // 15
}

7.2 函数作为值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 函数赋值给变量
add := func(a, b int) int {
return a + b
}

// 函数作为参数
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, v := range nums {
result[i] = fn(v)
}
return result
}

func main() {
double := func(x int) int { return x * 2 }
nums := []int{1, 2, 3}
fmt.Println(apply(nums, double)) // [2, 4, 6]
}

7.3 defer — 延迟执行

defer 在函数返回前执行,常用于资源清理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func readFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close() // 函数结束时自动关闭文件

// 读取文件...
return nil
}

// defer 的执行顺序:后进先出(栈)
func demo() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("hello")
}
// 输出:hello → 3 → 2 → 1

八、结构体与自定义类型

8.1 结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 定义结构体
type User struct {
ID int
Username string
Email string
Age int
IsActive bool
}

func main() {
// 创建实例
u1 := User{
ID: 1,
Username: "july",
Email: "july@example.com",
Age: 25,
}

// 按顺序初始化(不推荐,可读性差)
u2 := User{2, "zhangsan", "zhangsan@example.com", 30, true}

// 零值初始化,再赋值
var u3 User
u3.Username = "lisi"

// 匿名结构体
point := struct {
X, Y int
}{10, 20}

fmt.Println(u1, u2, u3, point)
}

8.2 方法(给类型绑定函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 值接收者方法(不修改原值)
func (u User) DisplayName() string {
return fmt.Sprintf("%s (%s)", u.Username, u.Email)
}

// 指针接收者方法(可以修改原值)
func (u *User) SetEmail(email string) {
u.Email = email
}

// 构造函数(Go 惯例)
func NewUser(username, email string) *User {
return &User{
Username: username,
Email: email,
IsActive: true,
}
}

func main() {
user := NewUser("july", "july@example.com")
fmt.Println(user.DisplayName()) // july (july@example.com)

user.SetEmail("new@example.com")
fmt.Println(user.Email) // new@example.com
}

九、接口(Interface)

Go 的接口是隐式实现的——不需要显式声明 implements,只要实现了接口的所有方法就算实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 定义接口
type Animal interface {
Speak() string
Move() string
}

// Cat 实现了 Animal 接口
type Cat struct {
Name string
}

func (c Cat) Speak() string {
return "喵喵喵"
}

func (c Cat) Move() string {
return "悄悄地走"
}

// Dog 实现了 Animal 接口
type Dog struct {
Name string
}

func (d Dog) Speak() string {
return "汪汪汪"
}

func (d Dog) Move() string {
return "跑着走"
}

// 使用接口
func MakeAnimalAct(a Animal) {
fmt.Printf("%T 说: %s, 移动: %s\n", a, a.Speak(), a.Move())
}

func main() {
cat := Cat{Name: "小花"}
dog := Dog{Name: "旺财"}

MakeAnimalAct(cat) // main.Cat 说: 喵喵喵, 移动: 悄悄地走
MakeAnimalAct(dog) // main.Dog 说: 汪汪汪, 移动: 跑着走

// 空接口 interface{}(或 any):可以接收任意类型
var anything any
anything = "hello"
anything = 42
anything = []int{1, 2, 3}
fmt.Println(anything)

// 类型断言
var a Animal = cat
c, ok := a.(Cat) // 断言为 Cat 类型
if ok {
fmt.Println("这是一只猫:", c.Name)
}
}

十、错误处理

Go 不使用 try-catch,而是通过多返回值显式处理错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import (
"errors"
"fmt"
"os"
)

// 自定义错误
func validateAge(age int) error {
if age < 0 {
return errors.New("年龄不能为负数")
}
if age > 150 {
return fmt.Errorf("年龄 %d 超出合理范围", age)
}
return nil
}

// 常见模式:返回 (result, error)
func readConfig(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("读取配置失败: %w", err) // %w 可以保留原始错误链
}
return string(data), nil
}

func main() {
// 检查错误
if err := validateAge(-5); err != nil {
fmt.Println("验证失败:", err)
}

config, err := readConfig("/etc/app.conf")
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println(config)
}

十一、并发编程(goroutine)⭐

并发是 Go 最大的亮点。

11.1 goroutine — 轻量级线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
"fmt"
"time"
)

// 普通函数
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Printf("%d ", i)
time.Sleep(100 * time.Millisecond)
}
}

func main() {
// 在前面加 go 关键字即可启动一个 goroutine
go printNumbers()

// 主 goroutine 继续执行
fmt.Println("主程序继续...")
time.Sleep(600 * time.Millisecond)
fmt.Println("\n主程序结束")
}

// 输出(顺序不确定):
// 主程序继续...
// 1 2 3 4 5
// 主程序结束

11.2 channel — goroutine 间通信

Go 的核心哲学:不要通过共享内存来通信,而要通过通信来共享内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 无缓冲 channel
func demo1() {
ch := make(chan string)

go func() {
ch <- "你好,来自 goroutine"
}()

msg := <-ch
fmt.Println(msg)
}

// 有缓冲 channel
func demo2() {
ch := make(chan int, 3) // 缓冲大小为 3

ch <- 1
ch <- 2
ch <- 3
// ch <- 4 // 会阻塞!缓冲已满

fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(<-ch) // 3
}

// 生产者-消费者模式
func producer(ch chan<- int) {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Printf("生产: %d\n", i)
}
close(ch)
}

func consumer(ch <-chan int) {
for num := range ch {
fmt.Printf("消费: %d\n", num)
}
}

func main() {
ch := make(chan int, 2)
go producer(ch)
consumer(ch)
}

11.3 select — 多路复用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func main() {
ch1 := make(chan string)
ch2 := make(chan string)

go func() {
time.Sleep(1 * time.Second)
ch1 <- "来自 ch1"
}()

go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自 ch2"
}()

for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("收到:", msg1)
case msg2 := <-ch2:
fmt.Println("收到:", msg2)
case <-time.After(3 * time.Second):
fmt.Println("超时!")
}
}
}

11.4 sync.WaitGroup — 等待 goroutine 完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import (
"fmt"
"sync"
)

func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成后计数器 -1
fmt.Printf("Worker %d 开始\n", id)
// 执行工作...
fmt.Printf("Worker %d 完成\n", id)
}

func main() {
var wg sync.WaitGroup

for i := 1; i <= 5; i++ {
wg.Add(1) // 计数器 +1
go worker(i, &wg)
}

wg.Wait() // 等待所有 goroutine 完成
fmt.Println("所有 worker 完成")
}

十二、包管理(Go Modules)

12.1 初始化模块

1
go mod init github.com/yourname/myproject

生成 go.mod 文件:

1
2
3
module github.com/yourname/myproject

go 1.22

12.2 安装依赖

1
2
go get github.com/gin-gonic/gin     # 安装 gin web 框架
go mod tidy # 清理未使用的依赖

12.3 自定义包

项目结构:

1
2
3
4
5
myproject/
├── go.mod
├── main.go
└── utils/
└── math.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// utils/math.go
package utils

func Add(a, b int) int {
return a + b
}

// 包级变量(首字母大写 = 公开导出)
var Version = "1.0.0"

// 私有函数(首字母小写)
func internalHelper() string {
return "内部函数"
}
1
2
3
4
5
6
7
8
9
10
11
12
// main.go
package main

import (
"fmt"
"github.com/yourname/myproject/utils"
)

func main() {
fmt.Println(utils.Add(1, 2)) // 使用自定义包
fmt.Println(utils.Version)
}

十三、实战:构建一个 HTTP 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package main

import (
"encoding/json"
"net/http"
"strconv"
"sync"
)

// 数据结构
type Article struct {
ID int `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}

// 模拟数据库
var (
articles = make(map[int]Article)
mu sync.RWMutex
nextID = 1
)

// 获取所有文章
func listArticles(w http.ResponseWriter, r *http.Request) {
mu.RLock()
defer mu.RUnlock()

var result []Article
for _, a := range articles {
result = append(result, a)
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}

// 创建文章
func createArticle(w http.ResponseWriter, r *http.Request) {
var article Article
json.NewDecoder(r.Body).Decode(&article)

mu.Lock()
article.ID = nextID
nextID++
articles[article.ID] = article
mu.Unlock()

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(article)
}

// 获取单篇文章
func getArticle(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") // Go 1.22+ 路由参数
id, _ := strconv.Atoi(idStr)

mu.RLock()
article, ok := articles[id]
mu.RUnlock()

if !ok {
http.NotFound(w, r)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(article)
}

// 删除文章
func deleteArticle(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, _ := strconv.Atoi(idStr)

mu.Lock()
delete(articles, id)
mu.Unlock()

w.WriteHeader(http.StatusNoContent)
}

func main() {
mux := http.NewServeMux()

mux.HandleFunc("GET /api/articles", listArticles)
mux.HandleFunc("POST /api/articles", createArticle)
mux.HandleFunc("GET /api/articles/{id}", getArticle)
mux.HandleFunc("DELETE /api/articles/{id}", deleteArticle)

fmt.Println("服务器启动于 http://localhost:8080")
http.ListenAndServe(":8080", mux)
}

启动并测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
go run main.go

# 另一个终端测试
# 创建文章
curl -X POST http://localhost:8080/api/articles \
-H "Content-Type: application/json" \
-d '{"title":"Go入门","content":"Go语言真好学"}'

# 获取文章列表
curl http://localhost:8080/api/articles

# 获取单篇文章
curl http://localhost:8080/api/articles/1

# 删除文章
curl -X DELETE http://localhost:8080/api/articles/1

常用 Go 命令速查

命令 说明
go run main.go 运行程序
go build 编译为二进制
go build -o app 指定输出文件名
go fmt ./... 格式化代码
go vet ./... 静态检查
go test ./... 运行测试
go mod init name 初始化模块
go mod tidy 整理依赖
go get pkg 安装依赖
go doc fmt.Println 查看文档
go env 查看环境变量
CGO_ENABLED=0 go build 静态编译(容器部署用)

进一步学习

  1. 标准库net/httpencoding/jsondatabase/sqlcontexttesting
  2. Web 框架:Gin、Echo、Fiber
  3. ORM:GORM
  4. 项目实战:写一个 CLI 工具(使用 cobra 库),或一个 RESTful API
  5. Go 箴言:理解 Go 的设计哲学——简洁、组合优于继承、显式错误处理

结语

Go 语言的核心理念是简洁即力量。它没有花哨的语法糖,没有继承和泛型(1.18 开始有有限的泛型),但正是这份克制让它成为构建可靠、高效系统的最佳选择之一。

本文覆盖了 Go 语言 80% 的常用知识点。建议从写一个小项目开始实践——比如一个简单的 Todo API 或文件处理工具,在实践中巩固所学。

写 Go 的第一天可能不习惯,一周后你会爱上它的简洁。 💙