隨手扎
【心得筆記】精通 Go 程式設計
簡評
怎麼說呢…看完當下的感覺是覺得,這本書很特別,我想這會許是因為這本書與Go語言設計者本身有關係。
Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;
Alan A. A. Donovan 是作者之一,從致謝頁來看,同時也是Google員工。而Go語言,又是Google所開發的一個簡潔、高效的程式語言。Alan很可能參與了Go語言的設計與開發,也很可能是最早使用Go語言的一批人。這或許說明為何此種,跟閱讀「松本行弘的程序設計」之後的感覺,是相似的。推薦大家閱讀。
推薦指數:★★★★☆
本書的前幾章節,可以直接在官網上閱讀。這本書也已經被翻譯為多國語言,包含中文。中文書名是精通 Go 程式設計。那麼接下來,一樣寫一些我自己覺得值得紀錄的話語。
佳句筆記
Go語言的設計
為了達到零配置的設計目標,Go語言的工具箱很多地方都依賴各種約定。
約定大於設定 這樣想法和Ruby很像,並且與我近期正在重看的「Haskell趣學指南」,感覺也和Haskell很像。Haskell的變數與函式定義不可以使用大寫開頭,因為大寫開頭是Types的標示方式;Ruby的Class Name同樣要大寫開始;Go語言如過要給外部package使用,也就是export變數、結構或是函式,也必須使用大寫開頭,連結構內的field如果要直接給外部使用,也是同樣規則。(Go語言支援UTF-8,並有關於中日文等字是否應該視為公開的討論,可能會在Go 2以後可以導出,但原本視為小寫)
A solution that's been kicking around for a while:
For Go 2 (can't do it before then): Change the definition to “lower case letters and are package-local; all else is exported”. Then with non-cased languages, such as Japanese, we can write 日本語 for an exported name and 日本語 for a local name. This rule has no effect, relative to the Go 1 rule, with cased languages. They behave exactly the same.
測試
其中有兩種技術在實踐中證明是比較有效的. 第一種是代碼在被正式部署前需要進行代碼評審. 第二種是測試, 是本章的討論主題.
Go語言包含了測試工具,可以使用go test
進行測試,並會依照約定執行測試。
(Go語言除了單元測試外,還可以依約定寫覆蓋率測試、基準測試。cpu、block、mem剖析等)
$ go test -cpuprofile=cpu.out
$ go test -blockprofile=block.out # 阻塞分析則記録了goroutine最大的阻塞操作, 例如繫統調用, 管道發送和接收, 還有獲取鎖等
$ go test -memprofile=mem.out
_test.go
爲後綴名- 必須引入
testing
package - 測試函數以
Test
或Benchmark
開頭 - 可以宣告一個虛擬測試包,以
_test
爲後綴名
簡潔
它沒有隱式的數值轉換,沒有構造函數和析構函數,沒有運算符重載,沒有默認參數,也沒有繼承,沒有泛型,沒有異常,沒有宏,沒有函數脩飾,更沒有線程局部存儲。
對…沒有默認參數,同樣沒有多載(overloading)、沒有巨集、沒有函數修飾、其goroutine也是極小成本。但是透過interface
,仍可以使用多形(polymorphism)。提倡組合,繼承也是用類似組合方式處理,不過因為是語法糖,有些時候使用仍要小心。
(相對來說Rust就真的難學多,有巨集(雖不及Common Lisp的read-macro,但也足夠強大,強大的匹配(match)模式))
平行處理
由於現代計算機是一個併行的機器,Go語言提供了基於CSP的併發特性支持。Go語言的動態棧使得輕量級線程goroutine的初始棧可以很小,因此創建一個goroutine的代價很小,創建百萬級的goroutine完全是可行的。
這也是Go語言最最大的特色。其他類似的還有Erlang,以及建立在Erlang上的Elixir1。Rust也具有這樣的特色,不過我還沒很好的了解。
自動加分號
然而在編譯時,編譯器會主動在一些特定的符號(譯註:比如行末是,一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、–、)、]或}中的一個) 後添加分號,所以在哪里加分號合適是取決於Go語言代碼的。
因為Go會自動加分號,所以區塊花括號的開頭不可換行,也就是下面這樣寫並不可以:
func hello(name string)
{
fmt.Println("Hello, " + name)
}
必須寫:
func hello(name string) {
fmt.Println("Hello, " + name)
}
規定一種標準的代碼格式可以規避掉無盡的無意義的撕逼(譯註:也導致了Go語言的TIOBE排名較低,因爲缺少撕逼的話題)。
go fmt
工具就是用來統一寫法的XD。
goimports
這里還有一個相關的工具,goimports,會自動地添加你代碼里需要用到的import聲明以及需要移除的import聲明。這個工具併沒有包含在標準的分發包中,然而你可以自行安裝:
go get golang.org/x/tools/cmd/goimports
可以夠過go get <uri>
下載package。但有時候不同專案可能需要使用不同版本的package:
通常的解決方案是使用vendor的目録用於存儲依賴包的固定版本的源代碼,對本地依賴的包的版本更新也是謹慎和持續可控的。在Go1.5之前,一般需要脩改包的導入路徑,所以複製後golang.org/x/net/html導入路徑可能會變爲gopl.io/vendor/golang.org/x/net/html。最新的Go語言命令已經支持vendor特性,但限於篇幅這里併不討論vendor的具體細節。不過可以通過go help gopath命令査看Vendor的幫助文檔。
Go語言目前也有許多相關的討論(PackageManagementTools)。
內部包
如同虛擬測試包,也可以建立內部包。內部包的內容在包外不可鍵,建立方式也只需要在路徑建立internal
資料夾。如果想為net/http
建立內部包,可以建立net/http/internal/chunked
。
自動記憶體回收
之前跟寫過一點關於記憶體回收的東西,也跟朋友討論過。這句話驗證我當時討論的想法。
(unsafe package)産生錯誤的原因很微妙。 有時候垃圾迴收器會移動一些變量以降低內存碎片等問題 。這類垃圾迴收器被稱爲移動GC。
