Go 語言從誕生那一刻到今天已經有十年多了,Go 語言的魅力使得其在全世界范圍內擁有了百萬級的擁躉1。那究竟是什么讓大量的開發(fā)人員學習 Go 或從其他語言轉向 Go 語言呢?筆者認為 Go 魅力的根源就來自于 Go 語言的設計哲學。
關于 Go 語言的設計哲學,Go 語言之父們以及 Go 核心團隊的開發(fā)者們并沒有給出明確的官方說法。但在這里我將根據我個人對他們以及 Go 社區(qū)主流觀點和代碼行為的整理、分析和總結,列出四條 Go 語言的設計哲學。理解這些設計哲學將對讀者形成 Go 原生編程思維、編寫高質量 Go 代碼起到積極的影響。
第一條原則: 追求簡單,少即是多
簡單是一種偉大的美德,但我們需要更艱苦地努力才能實現它,并需要經過一個教育的過程才能去欣賞和領會它。但糟糕的是:復雜的東西似乎更有市場。- 圖靈獎獲得者迪杰·斯特拉(1984)
通常當我們向 Gopher 們提出這樣一個問題:“你為什么喜歡 Go 語言”后,我們會得到很多種答案,諸如:
- 編譯速度快
- 執(zhí)行速度快
- 單一二進制文件,部署簡單
- 好棒的工具集
- 自帶的標準庫超級強大
- 內置并發(fā)
- interface 很棒
- 跨平臺 Easy
- … …
但在我們得到的眾多答案中:排名靠前而又占據多數的總是“Go 很簡單”,這也和官方 Go 語言調查的結果是一致的。
和那些通過相互借鑒而不斷增加新特性來吸引程序員眼球的主流編程語言相比,比如 C++、Java 等,Go 的設計者們在語言設計之初就選擇拒絕走語言特性融合的道路,選擇了“做減法”,選擇了“簡單”,他們把復雜性留給了語言自身的設計和實現,留給了 Go 核心開發(fā)組自身,而將簡單、易用和清晰留給了廣大 gopher 們。因此,今天呈現在我們在眼前的是這樣一門 Go 語言:
- 簡潔、常規(guī)的語法(不需要解析符號表),它僅有 25 個關鍵字
- 內置垃圾收集,降低開發(fā)人員內存管理的心智負擔
- 沒有頭文件
- 顯式依賴(package)
- 沒有循環(huán)依賴(package)
- 常量只是數字
- 頭母大小寫決定可見性
- 任何類型都可以擁有方法(沒有類)
- 沒有子類型繼承(沒有子類)
- 沒有算術轉換
- 接口是隱式的(無需“implements”聲明)
- 方法就是函數
- 接口只是方法集合(沒有數據)
- 方法僅按名稱匹配(不是按類型)
- 沒有構造函數或析構函數
- n++和 n–是語句,而不是表達式
- 沒有++n 和–n
- 賦值不是表達式
- 在賦值和函數調用中定義的求值順序(無“序列點”概念)
- 沒有指針算術
- 內存總是初始化為零值
- 沒有 const 或其他類型注解語法
- 沒有模板/泛型
- 沒有異常(exception)
- 內置字符串、切片(slice)、字典(map)類型
- 內置數組邊界檢查
- 內置并發(fā)支持
- … …
任何的設計都存在著權衡與折中(tradeoff)。我們看到 Go 設計者選擇的“簡單”體現在站在巨人肩膀上去除或優(yōu)化了以往語言中已被開發(fā)者證明為體驗不好或難于駕馭的語法元素和語言機制,并提出了自己的一些創(chuàng)新性的設計(比如:頭母大小寫決定可見性、內存分配初始零值、內置以 go 關鍵字實現的并發(fā)支持等)。并且 Go 設計者推崇“最小方式”思維,即一個事情僅有一種方式或數量盡可能少的方式去完成,這大大減少了開發(fā)人員在路徑方式選擇上以及理解其他人所選擇路徑方式上的心智負擔。
正如 Rob Pike 所說的那樣:“Go 語言實際上是復雜的,但只是讓大家感覺很簡單”。這句話背后的深意就是“簡單”選擇的背后則是 Go 語言自身實現層面的復雜,而這些復雜性被 Go 語言的設計者“隱藏”起來了,就比如我們通過一個簡單的關鍵字“go”就可以搞定并發(fā),這種簡單的背后其實是 Go 團隊縝密設計和持續(xù)付出的結果。
此外,Go 的簡單哲學還體現在 Go1 兼容性2的提出。對于面對工程問題解決的開發(fā)人員來說,Go1 大大降低了工程層面上語言版本升級所帶來的消耗,讓 Go 的工程實踐變得格外簡單。
Go1兼容性說明摘錄
Go1定義了兩件事:第一,語言的規(guī)范; 第二,一組核心API的規(guī)范,即Go標準庫的“標準包”。Go1的發(fā)布包括兩個編譯器套件gc和gccgo,以及核心庫本身。
編寫符合Go1規(guī)范的程序將在該規(guī)范的生命周期內繼續(xù)得到正確編譯和運行。在某個不確定的點上,可能會出現Go2規(guī)范,但在那之前,在Go1的未來版本(Go 1.1,Go 1.2等)下,今天工作的Go程序仍應該繼續(xù)正常工作。
兼容性體現在源碼級別上。版本之間無法保證已編譯軟件包的二進制兼容性。Go語言新版本發(fā)布后,源碼需要使用新版本Go重新編譯和鏈接。
API可能會增長,增加新的包和功能,但不會以破壞現有Go1代碼的方式進行。
從 Go 1.0 發(fā)布至今,Go1 兼容性始終被很好地遵守著,當初使用 Go 1.4 編寫的代碼如今也可以順利地通過最新的 Go 1.14 版本的編譯并正常運行起來。
就像前面引用的圖靈獎獲得者迪杰·斯特拉的名言所說的那樣,這種創(chuàng)新性的“簡單”設計并不是一開始就能得到程序員的理解的,但在真正使用 Go 之后,這種身處設計哲學層面的“簡單”便延伸到 Go 語言編程應用的方方面面,持續(xù)影響著 Go 語言編程思維的形成。
在 Go 演化進入關鍵階段(走向 Go23)的今天,有人曾經向 Go 核心團隊提出這樣一個問題:Go 后續(xù)演化的最大難點是什么?Go 的一名核心團隊成員回答道:“最大的難題是如何能繼續(xù)保持 Go 語言的簡單”。
Russ Cox 《How Many Go Developers Are There?》https://research.swtch.com/gophercount ??
Go1 兼容性 https://golang.org/doc/go1compat ??