Files
zhuangang/plan/week01-go-basics.md
T

10 KiB
Raw Blame History

转岗计划 · 第 1 周学习计划(Go 基础语法)

所属阶段:第 1 个月 Go 全栈基础。总纲见同目录 README.md。

目标:一周内掌握 Go 的核心语法,最后用纯标准库写出一个能跑的命令行“任务清单”小程序,为第 2 周用 Gin 写接口打基础。

每天建议投入 1.5 到 2 小时:先看资料 30 分钟,再动手写代码。代码可以让 AI 辅助,但每个文件都要自己能讲清楚每一行在做什么。

参考资料:


最终项目结构

一周结束时,你的练习目录应该长这样:

go-learning/
├── go.mod
├── day1/
│   └── main.go
├── day2/
│   ├── main.go
│   └── calc.go
├── day3/
│   ├── main.go
│   └── collections.go
├── day4/
│   ├── main.go
│   └── task.go
├── day5/
│   ├── main.go
│   ├── storage.go
│   └── errors.go
├── day6/
│   ├── main.go
│   ├── worker.go
│   └── calc_test.go
└── todo/                    # 第 7 天的周末小项目
    ├── go.mod
    ├── main.go
    ├── internal/
    │   ├── task/
    │   │   ├── task.go
    │   │   └── task_test.go
    │   └── store/
    │       └── file_store.go
    └── tasks.json           # 程序运行后自动生成

第 1 天:环境搭建和第一个程序

学习内容

  • 在 Ubuntu 上安装 Go:sudo apt install golang-go,或从 https://go.dev/dl/ 下载最新版。用 go version 确认安装成功。
  • 编辑器装好 Go 插件(VS Code 或 Cursor 里搜索 “Go” 官方插件)。
  • 理解 module(模块)、package(包)、main 函数。
  • 变量声明:var、:=,常量 const,基本类型 int、float64、string、bool。
  • fmt.Println、fmt.Printf 格式化输出。

要建的文件

  • go-learning/go.mod:在 go-learning 目录里运行 go mod init go-learning 生成。
  • day1/main.go:
    • 打印 “Hello, Go”。
    • 声明你的姓名、工作年限、技术栈(用变量和常量),格式化打印成一段自我介绍。
    • 试一下类型转换:把 int 转成 float64 计算平均值。

运行方式:go run ./day1

完成标准:能说清楚 var a int = 1 和 a := 1 的区别,以及为什么 Go 里声明了没用的变量会编译报错。


第 2 天:流程控制和函数

学习内容

  • if、for(Go 只有 for,没有 while)、switch。
  • 函数定义、多返回值、命名返回值。
  • 函数作为参数(和 JS 里的回调对比)、匿名函数、闭包。
  • defer 的执行顺序。

要建的文件

  • day2/calc.go:
    • func Add(a, b int) int
    • func Divide(a, b float64) (float64, error):除数为 0 时返回错误。
    • func Apply(nums []int, fn func(int) int) []int:对每个数执行传入的函数,类似 JS 的 map。
  • day2/main.go:
    • 调用上面三个函数并打印结果。
    • 用 for 打印 1 到 100 的 FizzBuzz。
    • 写一个 defer 的例子,观察打印顺序。

运行方式:go run ./day2

完成标准:理解 Go 为什么用“多返回值 + error”而不是 try/catch 来处理错误。


第 3 天:数组、切片和 map

学习内容

  • 数组和切片的区别,make、append、len、cap。
  • 切片的底层共享内存问题(这是面试常考点)。
  • map 的增删改查、判断 key 是否存在(v, ok := m[key])。
  • range 遍历。

要建的文件

  • day3/collections.go:
    • func Filter(nums []int, fn func(int) bool) []int:类似 JS 的 filter。
    • func Unique(items []string) []string:用 map 给字符串切片去重。
    • func WordCount(text string) map[string]int:统计一段英文里每个单词出现的次数(用 strings.Fields)。
  • day3/main.go:
    • 调用以上函数并打印结果。
    • 写一个例子演示:对一个切片截取子切片后修改元素,原切片也跟着变。

运行方式:go run ./day3

完成标准:能解释 append 什么时候会重新分配底层数组。


第 4 天:结构体、方法和接口(最重要的一天)

学习内容

  • struct 定义、初始化,和 JS 对象、TS 的 interface 对比。
  • 方法:值接收者和指针接收者的区别。
  • 接口 interface:Go 的接口是隐式实现的,不需要写 implements。
  • 结构体标签 json:"title" 和 encoding/json 的序列化、反序列化。

要建的文件

  • day4/task.go:
    • 定义 Task 结构体:ID int、Title string、Done bool、CreatedAt time.Time,并加上 JSON 标签。
    • 方法 func (t *Task) MarkDone():把任务标记为完成。
    • 方法 func (t Task) String() string:返回类似 [x] 1. 学习 Go 的文字。
    • 定义接口 type Store interface { Add(t Task) error; List() ([]Task, error) }。
    • 实现一个 MemoryStore 结构体,用切片在内存里存任务,满足 Store 接口。
  • day4/main.go:
    • 创建几个任务,存入 MemoryStore,标记其中一个完成,打印列表。
    • 把任务列表用 json.MarshalIndent 转成 JSON 字符串打印出来。

运行方式:go run ./day4

完成标准:能说清楚什么时候用指针接收者,以及“接口隐式实现”带来的好处。


第 5 天:错误处理和文件读写

学习内容

  • error 接口、errors.New、fmt.Errorf 搭配 %w 包装错误。
  • errors.Is、errors.As 判断错误类型。
  • 自定义错误类型。
  • os.ReadFile、os.WriteFile 读写文件。
  • panic 和 recover(了解即可,业务代码里尽量不用)。

要建的文件

  • day5/errors.go:
    • 定义 var ErrNotFound = errors.New("task not found")。
    • 定义自定义错误 ValidationError 结构体,包含字段名和原因,实现 Error() 方法。
  • day5/storage.go:
    • func SaveTasks(path string, tasks []Task) error:把任务列表写成 JSON 文件。
    • func LoadTasks(path string) ([]Task, error):从 JSON 文件读取;文件不存在时返回空列表而不是报错。
    • func FindTask(tasks []Task, id int) (Task, error):找不到时返回包装后的 ErrNotFound。
  • day5/main.go:
    • 保存任务到 tasks.json,再读回来打印。
    • 查找一个不存在的 ID,用 errors.Is 判断是不是 ErrNotFound。
    • 标题为空时返回 ValidationError,用 errors.As 取出字段名打印。

运行方式:go run ./day5

完成标准:习惯每个可能出错的调用后面都写 if err != nil,并给错误加上上下文信息。


第 6 天:并发入门和单元测试

学习内容

  • goroutine:用 go 关键字启动并发任务。
  • channel:在 goroutine 之间传数据,有缓冲和无缓冲的区别。
  • sync.WaitGroup 等待一组任务完成,sync.Mutex 保护共享数据。
  • 用 go test 写单元测试,表格驱动测试的写法。

要建的文件

  • day6/worker.go:
    • func FetchAll(urls []string) map[string]int:并发请求多个网址(用 net/http),返回每个网址的 HTTP 状态码。用 WaitGroup 等待全部完成,用 Mutex 保护结果 map。
    • func Pipeline(nums []int) <-chan int:用 channel 写一个“生成数字,然后平方”的流水线。
  • day6/main.go:调用 FetchAll 请求 3 到 5 个网站,打印结果和总耗时,对比顺序请求的耗时。
  • day6/calc_test.go:把第 2 天的 Add 和 Divide 复制到 day6 里,写表格驱动测试,覆盖正常情况和除数为 0 的情况。

运行方式:go run ./day6 和 go test ./day6 -v

完成标准:能解释 goroutine 和 JS 的 Promise/async 有什么不同。


第 7 天:周末小项目 · 命令行任务清单

把这周学的东西串起来,做一个能实际使用的命令行工具。

功能

  • go run . add "学习 Gin":添加任务。
  • go run . list:列出所有任务。
  • go run . done 1:把 1 号任务标记为完成。
  • go run . delete 1:删除 1 号任务。
  • 数据保存在 tasks.json,程序重启后还在。

要建的文件

  • todo/go.mod:在 todo 目录运行 go mod init todo。
  • todo/internal/task/task.go:Task 结构体和它的方法(来自第 4 天),加上 Validate() 方法校验标题不能为空。
  • todo/internal/task/task_test.go:测试 MarkDone 和 Validate。
  • todo/internal/store/file_store.go:FileStore 结构体,实现 Add、List、Done、Delete,内部用 JSON 文件持久化(来自第 5 天),找不到任务时返回 ErrNotFound。
  • todo/main.go:用 os.Args 解析命令,调用 FileStore,出错时打印友好的提示并以非 0 状态码退出。

运行方式:在 todo 目录下运行 go run . list,测试用 go test ./...

完成标准

  • 四个命令都能正常使用,重启后数据不丢。
  • go test ./... 全部通过。
  • go vet ./... 没有警告,代码用 gofmt 格式化过。
  • 给 todo 写一个简单的 README,提交并推送到 zhuangang 仓库(路径 go-learning/todo/),这是你的第一个 Go 作品。以后整理作品集时可以再单独同步到 GitHub。

本周自查清单

  • 能用 := 和 var 声明变量,知道区别
  • 会写多返回值函数,并正确处理 error
  • 理解切片和 map 的常见用法与坑
  • 会定义结构体、方法和接口
  • 会读写 JSON 文件
  • 会用 goroutine、channel、WaitGroup 写简单并发
  • 会写表格驱动的单元测试
  • 完成 todo 项目并推送到 zhuangang 仓库
  • 在总纲 README.md 的进度记录里勾掉第 1 周

下周预告:第 2 周用 Gin 把这个任务清单改造成 HTTP 接口,前端用你熟悉的 React 或 Vue 接上去。