Go語言"入門易,精通難”。想要用Go語言寫出優(yōu)質(zhì)的軟件,不僅要了解Go語言的語法,還需要對Go語言的特性、軟件的通用編寫方法、軟件項(xiàng)目的組織方法、并發(fā)程序設(shè)計(jì)、軟件測試、軟件性能優(yōu)化等方面都有一定的了解。 本書既聚焦于Go語言,又不限于Go語言,介紹了開發(fā)者在使用Go語言時(shí)經(jīng)常犯的100個(gè)經(jīng)典錯(cuò)誤,內(nèi)容側(cè)重于語言核心和標(biāo)準(zhǔn)庫。對大多數(shù)錯(cuò)誤的討論都提供了具體的示例,以說明在什么時(shí)候容易犯這樣的錯(cuò)誤。這不是一本教條主義的圖書,每個(gè)解決方案都詳細(xì)傳達(dá)了它應(yīng)該適用的上下文。
Teiva Harsanyi 是Docker 公司的資深軟件工程師,常年研究Go語言及如何設(shè)計(jì)和實(shí)現(xiàn)可靠的應(yīng)用程序,具有非常豐富的實(shí)戰(zhàn)經(jīng)驗(yàn)。
Go語言翻譯小組成員:曾浩浩 、晁岳攀 等,譯者均為資深軟件工程師,對Go語言編寫及軟件項(xiàng)目實(shí)現(xiàn)有豐富的實(shí)戰(zhàn)經(jīng)驗(yàn)。
1 Go:入門易,精通難
1.1 Go 語言概述
1.2 簡單不意味著容易
1.3 100 個(gè)Go 語言錯(cuò)誤
1.3.1 bug
1.3.2 不必要的復(fù)雜性
1.3.3 可讀性差
1.3.4 非最佳組織形式
1.3.5 API 對用戶不友好
1.3.6 代碼有待優(yōu)化
1.3.7 效率低
總結(jié)
2 代碼和項(xiàng)目組織
2.1 #1:意想不到的變量隱藏
2.2 #2:不必要的嵌套代碼
2.3 #3:濫用init 函數(shù)
2.3.1 概念
2.3.2 何時(shí)使用init 函數(shù)
2.4 #4:過度使用getter 和setter
2.5 #5:避免接口污染
2.5.1 概念
2.5.2 何時(shí)使用接口
2.5.3 接口污染
2.6 #6:在生產(chǎn)者端的接口
2.7 #7:返回接口
2.8 #8:any 意味著nothing
2.9 #9:不知道什么時(shí)候使用泛型
2.9.1 概念
2.9.2 常見的使用方法和誤用
2.10 #10:沒有意識到類型嵌入可能存在的問題
2.11 #11:不使用函數(shù)式選項(xiàng)模式
2.11.1 配置結(jié)構(gòu)體
2.11.2 生成器模式
2.11.3 函數(shù)式選項(xiàng)模式
2.12 #12:項(xiàng)目組織混亂
2.12.1 項(xiàng)目結(jié)構(gòu)
2.12.2 包組織
2.13 #13:創(chuàng)建實(shí)用程序包
2.14 #14:忽略包名稱沖突
2.15 #15:缺少代碼文檔
2.16 #16:不使用代碼檢查工具
總結(jié)
3 數(shù)據(jù)類型
3.1 #17:使用八進(jìn)制字面量會(huì)帶來混淆
3.2 #18:容易忽視的整數(shù)溢出
3.2.1 概念
3.2.2 在遞增操作時(shí)檢測整數(shù)溢出
3.2.3 在加法操作中檢測整數(shù)溢出
3.2.4 在乘法操作中檢測整數(shù)溢出
3.3 #19:不了解浮點(diǎn)數(shù)
3.4 #20:不了解切片的長度和容量
3.5 #21:低效的切片初始化
3.6 #22:對 nil 和空切片的困惑 .
3.7 #23:未正確檢查切片是否為空
3.8 #24:無法正確復(fù)制切片
3.9 #25:使用append 的副作用
3.10 #26:切片和內(nèi)存泄漏
3.10.1 容量泄漏
3.10.2 切片和指針
3.11 #27:低效的 map 初始化
3.11.1 概念
3.11.2 初始化
3.12 #28:map 和內(nèi)存泄漏
3.13 #29:比較值時(shí)發(fā)生的錯(cuò)誤
總結(jié)
4 控制結(jié)構(gòu)
4.1 #30:忽視在 range 循環(huán)中元素被復(fù)制的事實(shí)
4.1.1 概念
4.1.2 值復(fù)制
4.2 #31:忽視 range 循環(huán)中參數(shù)是如何求值的
4.2.1 channel
4.2.2 數(shù)組
4.3 #32:忽視在 range 循環(huán)中使用指針元素的影響
4.4 #33:在 map 迭代過程中做出錯(cuò)誤假設(shè)
4.4.1 排序
4.4.2 在迭代時(shí)往 map 中添加數(shù)據(jù)
4.5 #34:忽視break 語句是如何工作的
4.6 #35:在循環(huán)中使用defer
總結(jié)
5 字符串
5.1 #36:不理解 rune 的概念
5.2 #37:字符串迭代不準(zhǔn)確
5.3 #38:亂用 trim 函數(shù)
5.4 #39:缺乏優(yōu)化的字符串連接
5.5 #40:無用的字符串轉(zhuǎn)換
5.6 #41:子字符串和內(nèi)存泄漏
總結(jié)
6 函數(shù)與方法
6.1 #42:不知道使用什么類型的接收器
6.2 #43:不要使用命名的結(jié)果參數(shù)
6.3 #44:使用命名的結(jié)果參數(shù)的意外副作用
6.4 #45:返回一個(gè) nil 接收器
6.5 #46:使用文件名作為函數(shù)輸入
6.6 #47:忽略defer 語句參數(shù)和接收器的計(jì)算
6.6.1 參數(shù)計(jì)算
6.6.2 指針和值接收器
總結(jié)
7 錯(cuò)誤管理
7.1 #48:panic
7.2 #49:搞不清何時(shí)需要包裝錯(cuò)誤
7.3 #50:不準(zhǔn)確的錯(cuò)誤類型檢查
7.4 #51:錯(cuò)誤地檢查錯(cuò)誤值
7.5 #52:處理同一個(gè)錯(cuò)誤兩次
7.6 #53:忽略錯(cuò)誤
7.7 #54:忽略 defer 語句返回的錯(cuò)誤
總結(jié)
8 并發(fā):基礎(chǔ)
8.1 #55:混淆并發(fā)和并行
8.2 #56:認(rèn)為并發(fā)總是更快
8.2.1 Go 調(diào)度
8.2.2 并行歸并排序
8.3 #57:對何時(shí)使用channel 或互斥鎖感到困惑
8.4 #58:不理解競爭問題
8.4.1 數(shù)據(jù)競爭與競爭條件
8.4.2 Go 內(nèi)存模型
8.5 #59:不了解工作負(fù)載類型對并發(fā)的影響
8.6 #60:誤解 Go 上下文
8.6.1 最后期限
8.6.2 取消信號
8.6.3 上下文值
8.6.4 感知上下文的取消信號
總結(jié)
9 并發(fā):實(shí)踐
9.1 #61:傳播不恰當(dāng)?shù)纳舷挛?br />
9.2 #62:在不知道何時(shí)停止的情況下啟動(dòng) goroutine
9.3 #63:沒有小心處理 goroutine 和循環(huán)變量
9.4 #64:使用 select 和channel 來期待確定性行為
9.5 #65:沒有使用通知channel
9.6 #66:沒有使用 nil channel
9.7 #67:對channel 緩沖區(qū)大小感到困惑
9.8 #68:忘記字符串格式化可能產(chǎn)生的副作用
9.8.1 etcd 數(shù)據(jù)競爭
9.8.2 死鎖
9.9 #69:使用 append 函數(shù)創(chuàng)造了數(shù)據(jù)競爭
9.10 #70:對切片和 map 不準(zhǔn)確地使用互斥鎖
9.11 #71:錯(cuò)誤使用 sync.WaitGroup
9.12 #72:忘記了 sync.Cond
9.13 #73:沒有使用 errgroup
9.14 #74:復(fù)制sync 類型
總結(jié)
10 標(biāo)準(zhǔn)庫
10.1 #75:提供錯(cuò)誤的持續(xù)時(shí)間
10.2 #76:time.After 和內(nèi)存泄漏
10.3 #77:常見的JSON 處理錯(cuò)誤
10.3.1 由嵌入式字段導(dǎo)致的非預(yù)期行為
10.3.2 JSON 和單調(diào)時(shí)鐘
10.3.3 map 中的any 類型
10.4 #78:常見的 SQL 錯(cuò)誤
10.4.1 忘記sql.Open 不一定與數(shù)據(jù)庫建立連接
10.4.2 忘記連接池導(dǎo)致的問題
10.4.3 未使用預(yù)準(zhǔn)備的語句
10.4.4 對空值處理不當(dāng)
10.4.5 沒有處理行迭代錯(cuò)誤
10.5 #79:沒有關(guān)閉瞬時(shí)資源
10.5.1 HTTP Body
10.5.2 sql.Rows
10.5.3 os.File
10.6 #80:在響應(yīng)HTTP 請求后忘記加return 語句
10.7 #81:使用默認(rèn)的HTTP 客戶端和服務(wù)端
10.7.1 HTTP 客戶端
10.7.2 HTTP 服務(wù)端
總結(jié)
11 測試
11.1 #82:未區(qū)分測試種類
11.1.1 build 標(biāo)識
11.1.2 環(huán)境變量
11.1.3 短模式
11.2 #83:未打開-race 開關(guān)
11.3 #84:未使用測試執(zhí)行模式
11.3.1 parallel 標(biāo)識
11.3.2 shuffle 標(biāo)識
11.4 #85:未使用表格驅(qū)動(dòng)型測試
11.5 #86:在單元測試中休眠
11.6 #87:沒有有效處理 time API
11.7 #88:未使用測試工具包
11.7.1 httptest 包
11.7.2 iotest 包
11.8 #89:寫出不準(zhǔn)確的基準(zhǔn)測試
11.8.1 未重置或暫停計(jì)時(shí)器
11.8.2 對微基準(zhǔn)測試做出錯(cuò)誤假設(shè)
11.8.3 未注意編譯器優(yōu)化
11.8.4 被觀察者效應(yīng)愚弄
11.9 #90:未探索所有的 Go 測試特性
11.9.1 代碼覆蓋率
11.9.2 從一個(gè)不同的包進(jìn)行測試
11.9.3 工具函數(shù)
11.9.4 設(shè)置和拆卸
總結(jié)
12 優(yōu)化
12.1 #91:不了解 CPU 緩存
12.1.1 CPU 架構(gòu)
12.1.2 緩存行
12.1.3 包含結(jié)構(gòu)體的切片 vs 包含切片的結(jié)構(gòu)體
12.1.4 可預(yù)測性
12.1.5 緩存放置策略
12.2 #92:編寫導(dǎo)致偽共享的并發(fā)代碼
12.3 #93:不考慮指令級并行性
12.4 #94:不了解數(shù)據(jù)對齊
12.5 #95:不了解棧與堆
12.5.1 棧 vs 堆
12.5.2 逃逸分析
12.6 #96:不了解如何減少分配
12.6.1 修改 API
12.6.2 編譯器優(yōu)化
12.6.3 sync.Pool
12.7 #97:沒有依賴內(nèi)聯(lián)
12.8 #98:沒有使用Go 診斷工具
12.8.1 分析工具
12.8.2 跟蹤工具
12.9 #99:不了解 GC 的工作原理
12.9.1 概念
12.9.2 示例
12.10 #100:不了解在 Docker 和 Kubernetes 中運(yùn)行Go 程序的影響
總結(jié)
結(jié)語